merge 64-bit fixes from Fernando Lucas Rodriguez <fernando_lr@terra.es>
[debian/gcpegg] / network.c
1 /* PROGRAM:     eggsh
2  * FILE:        $Header: /home/egg/src/RCS/network.c,v 1.7 1999/02/28 20:14:20 ghn Exp $
3  * PURPOSE:     Network communication functions
4  * AUTHOR:      Greg Nelson
5  * DATE:        98-05-09
6  *
7  * REVISED:
8  * $Log: network.c,v $
9  * Revision 1.7  1999/02/28 20:14:20  ghn
10  * Version 5.1: Modified InitNetwork to take interface (addr) argument as
11  * well as port, and handle various cases of interface (NULL, IP,
12  * hostname) gracefully.  Modified NetUp to call this new version.
13  *
14  * Revision 1.6  1999/01/02 00:01:01  ghn
15  * Socket and sockaddr_in corrections suggested by Mike Cheponis
16  * incorporated.
17  * perror() turned off for ENETUNREACH/EHOSTUNREACH unless app specifies
18  * "gripe" argument to NetTalk.
19  *
20  * Revision 1.5  1998/12/31 22:07:56  ghn
21  * Rev 5 code: includes multi-reg support, HTML, etc.
22  *
23  * Revision 1.4  1998/08/03 20:39:03  kelvin
24  * inet_ntoa warning fix, bad packet diagnostics.
25  *
26  * Revision 1.3  1998/08/01  18:51:25  ghn
27  * Added John's byte-order-independence changes.
28  *
29  * Revision 1.2  1998/08/01 17:16:39  ghn
30  * Added DND support and John's typecasts.
31  *
32  * Revision 1.1  1998/07/21 11:36:45  ghn
33  * Initial revision
34  *
35  * Copyright 1998 - Greg Nelson
36  * Redistributable under the terms of the GNU Public Licence (GPL)
37  */
38
39 #include <stdio.h>
40 #include <time.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <netdb.h>
44 #include <errno.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <sys/time.h>
50 #include <sys/ioctl.h>
51 #include <sys/utsname.h>
52 #include <string.h>
53 #include "global.h"
54 #include "genlib.h"
55 #include "network.h"
56 #include "errnos.h"
57
58 #define MAXBUFSIZE      512
59 char            buffer[MAXBUFSIZE];
60
61 /* Eventually, these might deal with encryption and decryption or
62    validation processes, but until such time as we actually do
63    something with these, they are no-ops. */
64 #define PktEncrypt(buf, len)    ERR_NONE
65 #define PktDecrypt(buf, len)    ERR_NONE
66
67 #ifdef HEXDUMP
68 extern void xd(FILE *out, void *bub, int bufl, int dochar);
69 #endif
70
71 int32 InitNetwork(char *addr, int32 port) {
72   struct protoent       *pp;
73   struct hostent        *hp;
74   struct sockaddr_in    sin;
75   struct utsname        uts;
76   int32                 sd;
77   int32                 argp;
78
79   if ((pp = getprotobyname("udp")) == NULL) {
80     perror("getprotobyname");
81     exit(-1);
82   }
83
84   /* Optimistically: */
85   memset(&sin, 0, sizeof(struct sockaddr_in));
86   sin.sin_port = htons(port);
87   sin.sin_family = AF_INET;
88
89   /* Try to convert dotted quad.  Hope this is okay for null addr? */
90   if (!addr || !inet_aton(addr, &(sin.sin_addr))) {
91     /* Nope, so try to gethostbyname */
92     if (!addr) {
93       /* No addr at all, use host name from uname */
94       if (uname(&uts) < 0) {
95         perror("uname");
96         exit(-1);
97       }
98     } else {
99       strcpy(uts.nodename, addr);
100     }
101     
102     if ((hp = gethostbyname(uts.nodename)) == NULL) {
103       fprintf(stderr, "gethostbyname(%s): %s", uts.nodename, strerror(errno));
104       exit(-1);
105     }
106
107     if (hp->h_addrtype != AF_INET) {
108       fprintf(stderr, "Host is not on the internet!\n");
109       exit(-1);
110     }
111
112     memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
113   }
114
115   if ((sd = socket(PF_INET, SOCK_DGRAM, 0)) <= 0) {
116     printf("Could not make socket\n");
117     exit(-1);
118   }
119   
120   if (bind(sd, (struct sockaddr *)(&sin), sizeof(sin)) != 0) {
121     if (errno == EADDRNOTAVAIL) {
122       /* Network probably not up yet. */
123       close(sd);        /* Error pointed out my Mike Cheponis */
124       return ERR_NOREPLY;
125     }
126     perror("bind");
127     exit(-1);
128   }
129
130   argp = 1;
131 #ifndef Solaris
132   if (ioctl(sd, FIONBIO, (char *)&argp) != 0) {
133     perror("ioctl FIONBIO");
134     exit(-1);
135   }
136 #endif
137    
138   return sd;
139 }
140
141 /* Bring net up with command */
142 int32 NetUp(char *cmd, char *addr, int32 port) {
143   int res;
144
145   res = system(cmd);
146   /* 0x7e00 = perm denied
147      0x7f00 = command not found
148      256 * cmd if command runs */
149   if (res != 0) return ERR_OTHER;
150
151   return InitNetwork(addr, port);
152 }
153
154 /* Bring net down with command */
155 int NetDown(char *cmd, int32 oldsd) {
156   int res;
157
158   /* Close existing connection */
159   if (oldsd >= 0) close(oldsd);
160
161   res = system(cmd);
162   if (res != 0) return ERR_OTHER;
163
164   return ERR_NONE;
165 }
166
167 int NetGetAddr(struct sockaddr_in *sin, char *host, uint16 port) {
168   struct hostent        *hp;
169
170   if ((hp = gethostbyname(host)) == NULL) {
171     fprintf(stderr, "gethostbyname(%s): %s", host, strerror(errno));
172     return ERR_INRANGE;
173   }
174
175   if (hp->h_addrtype != AF_INET) {
176     fprintf(stderr, "Host is not on the internet!\n");
177     return ERR_INRANGE;
178   }
179
180   memset(sin, 0, sizeof(struct sockaddr_in));
181   sin->sin_port = htons(port);
182   sin->sin_family = AF_INET;
183   memcpy(&(sin->sin_addr), hp->h_addr, hp->h_length);
184   
185   return ERR_NONE;
186 }
187
188 /* Listen for a data request.
189
190    Listen on specified socket sd.  When one is received, validate the
191    checksum, and, if successful, allocate memory and stuff it as a
192    character array.  Return the remote sockaddr_in, if the sin pointer
193    is not null, and block until something interesting happens if block
194    is true. */
195 int NetListen(int sd, char **pktbuf, 
196               struct sockaddr_in *sin,
197               int block) {
198   fd_set                fdset;
199   struct timeval        timeout, *top;
200   int32                 nfound;
201   int32                 size;
202   int32                 count;
203   int32                 res;
204   short                 pktsize;
205   uint16                pkttype, cksumt, cksumc;
206
207   if (sd < 0) return ERR_INRANGE;
208
209   FD_ZERO(&fdset);
210   FD_SET(sd, &fdset);
211   timeout.tv_sec = timeout.tv_usec = 0;
212
213   /* Wait for an incoming connection.
214      If block is true, we wait indefinitely; otherwise, we
215      return immediately because of zero timeout. */
216
217   if (block) top = NULL; else top = &timeout;
218   nfound = select(FD_SETSIZE, &fdset, 0, 0, top);
219
220   if (nfound < 0) {
221     if (errno == EWOULDBLOCK) return ERR_COMM_TMOUT;
222     perror("select");
223     return ERR_OTHER;
224   }
225
226   /* No connections, go back to main loop. */
227   if (nfound == 0) return ERR_COMM_TMOUT;
228
229 #ifdef DEBUG
230   fprintf(stderr, "Net port got a request!\n");
231 #endif
232
233   if (!FD_ISSET(sd, &fdset)) {
234     fprintf(stderr, "Confused condition -- FD not part of set.\n");
235     return 0;
236   }
237
238   size = sizeof(*sin);
239   count = recvfrom(sd, buffer, MAXBUFSIZE, 0,
240                    (struct sockaddr *)sin, (socklen_t *)&size);
241   if (count < 0) {
242     /* Don't wait for it. */
243     if (errno == EWOULDBLOCK) return ERR_COMM_TMOUT;
244     perror("recvfrom");
245     return ERR_OTHER;
246   }
247
248 #ifdef HEXDUMP
249   fprintf(stderr, "Received %d bytes from %s\n", count,
250     inet_ntoa(sin->sin_addr));
251   xd(stderr, buffer, count, FALSE);
252 #endif
253
254   /* Mangle buffer as needed */
255   if ((res = PktDecrypt(buffer, count)) < 0) {
256     fprintf(stderr, "Packet decryption failed with %d.\n", (int)res);
257     return res;
258   }
259
260   /* Verify length and CRC of packet.  In making these
261      protocol-level sanity checks, we transform the relevant
262      fields in the packet from network to host byte order
263      as required.  Note, however, that the packet returned
264      to the caller remains, in its entirety, in network
265      byte order and even references to protocol-common
266      fields, if made, must be converted by the code which
267      invokes this function. */
268
269   { uint16 rpkttype, rpktsize, rcrc;
270
271     memcpy(&rpkttype, buffer, sizeof rpkttype);
272     memcpy(&rpktsize, buffer + 2, sizeof rpktsize);
273     memcpy(&rcrc, buffer + (count - 2), sizeof rcrc);
274     pktsize = ntohs(rpktsize);
275     if (pktsize != count) {
276 #ifdef DEBUG
277         fprintf(stderr, "** Bad packet length: pktsize = %d, count = %d.\n",
278             pktsize, count);
279 #endif
280         return ERR_PKT_BADLEN;
281     }
282     cksumt = ntohs(rcrc);
283     cksumc = BlockCRC16((byte *) buffer, count - 2);
284     if (cksumc != cksumt) {
285 #ifdef DEBUG
286         fprintf(stderr, "** Bad packet CRC: packet = %04X, computed = %04X.\n",
287             cksumt, cksumc);
288 #endif
289         return ERR_PKT_CKSUM;
290     }
291     pkttype = ntohs(rpkttype);
292   }
293
294 #ifdef DEBUG
295   fprintf(stderr, "Recv packet type %04x, %d bytes (hdr), cks = %04x\n",
296           pkttype, pktsize, cksumt);
297 #endif /* DEBUG */
298   
299   /* Hand back the packet */
300   *pktbuf = (char *)malloc(count);
301   memcpy(*pktbuf, buffer, count);
302
303   return ERR_NONE;
304 }
305
306 /* Send a data packet.
307
308    Checksum the packet, create a socket, and send it to the specified
309    port of the specified host.  Length of packet to be extracted from
310    packet header. */
311
312   /* The entire contents of the packet passed to NetTalk must
313      be in network byte order.  Fields within the packet used
314      to append the CRC, determine the number of bytes to send,
315      etc. are converted to host byte order as needed. */
316
317 int NetTalk(struct sockaddr_in *sin, char *pkt, int gripe) {
318   uint16                pktsize, cksum;
319   static struct protoent *pp = NULL;
320   int                   i, out_sock;
321
322   memcpy(&pktsize, pkt + 2, 2);
323   pktsize = ntohs(pktsize);
324 #ifdef DEBUG
325   if (pktsize > 1500) {
326     fprintf(stderr, "Bogus packet size of %ud bytes.  Suspect byte alignment bug.\n", pktsize);
327     abort();
328   }
329 #endif
330   cksum = htons(BlockCRC16((byte *) pkt, pktsize - sizeof(uint16)));
331   memcpy(pkt+pktsize-sizeof(uint16), &cksum, sizeof(uint16));
332
333   if (pp == NULL) {                   /* Only get protocol code the first time */
334     if ((pp = getprotobyname("udp")) == NULL) {
335       perror("getprotobyname");
336       return ERR_OTHER;
337     }
338   }
339
340   if ((out_sock = socket(AF_INET, SOCK_DGRAM, pp->p_proto)) < 0) {
341     perror("socket");
342     return ERR_OTHER;
343   }
344         
345   i = sendto(out_sock, pkt, pktsize, 
346              0, (struct sockaddr *)sin,
347              sizeof(struct sockaddr));
348   if (i == -1) {
349     /* We only report on certain errors if told to "gripe", because
350        it is a normal occurence if in PERM mode with a variably
351        available net connection. */
352     if ((errno != ENETUNREACH && errno != EHOSTUNREACH) || gripe) perror("sendto");
353     close(out_sock);
354     return ERR_OTHER;
355   }
356
357 #ifdef HEXDUMP
358   fprintf(stderr, "Sent %d bytes to %s\n", pktsize, inet_ntoa(sin->sin_addr));
359   xd(stderr, pkt, pktsize, FALSE);
360 #endif
361
362   i = close(out_sock);
363   if (i < 0) {
364     perror("close");
365   }
366   
367   return ERR_NONE;
368 }