File: network.c

package info (click to toggle)
gcpegg 5.1-14
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 324 kB
  • ctags: 439
  • sloc: ansic: 3,726; makefile: 97; sh: 33; csh: 21
file content (368 lines) | stat: -rw-r--r-- 9,867 bytes parent folder | download | duplicates (3)
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/* PROGRAM:	eggsh
 * FILE:	$Header: /home/egg/src/RCS/network.c,v 1.7 1999/02/28 20:14:20 ghn Exp $
 * PURPOSE:	Network communication functions
 * AUTHOR:	Greg Nelson
 * DATE:	98-05-09
 *
 * REVISED:
 * $Log: network.c,v $
 * Revision 1.7  1999/02/28 20:14:20  ghn
 * Version 5.1: Modified InitNetwork to take interface (addr) argument as
 * well as port, and handle various cases of interface (NULL, IP,
 * hostname) gracefully.  Modified NetUp to call this new version.
 *
 * Revision 1.6  1999/01/02 00:01:01  ghn
 * Socket and sockaddr_in corrections suggested by Mike Cheponis
 * incorporated.
 * perror() turned off for ENETUNREACH/EHOSTUNREACH unless app specifies
 * "gripe" argument to NetTalk.
 *
 * Revision 1.5  1998/12/31 22:07:56  ghn
 * Rev 5 code: includes multi-reg support, HTML, etc.
 *
 * Revision 1.4  1998/08/03 20:39:03  kelvin
 * inet_ntoa warning fix, bad packet diagnostics.
 *
 * Revision 1.3  1998/08/01  18:51:25  ghn
 * Added John's byte-order-independence changes.
 *
 * Revision 1.2  1998/08/01 17:16:39  ghn
 * Added DND support and John's typecasts.
 *
 * Revision 1.1  1998/07/21 11:36:45  ghn
 * Initial revision
 *
 * Copyright 1998 - Greg Nelson
 * Redistributable under the terms of the GNU Public Licence (GPL)
 */

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <string.h>
#include "global.h"
#include "genlib.h"
#include "network.h"
#include "errnos.h"

#define MAXBUFSIZE	512
char		buffer[MAXBUFSIZE];

/* Eventually, these might deal with encryption and decryption or
   validation processes, but until such time as we actually do
   something with these, they are no-ops. */
#define PktEncrypt(buf, len)	ERR_NONE
#define PktDecrypt(buf, len)	ERR_NONE

#ifdef HEXDUMP
extern void xd(FILE *out, void *bub, int bufl, int dochar);
#endif

int32 InitNetwork(char *addr, int32 port) {
  struct protoent	*pp;
  struct hostent	*hp;
  struct sockaddr_in	sin;
  struct utsname	uts;
  int32 		sd;
  int32 		argp;

  if ((pp = getprotobyname("udp")) == NULL) {
    perror("getprotobyname");
    exit(-1);
  }

  /* Optimistically: */
  memset(&sin, 0, sizeof(struct sockaddr_in));
  sin.sin_port = htons(port);
  sin.sin_family = AF_INET;

  /* Try to convert dotted quad.  Hope this is okay for null addr? */
  if (!addr || !inet_aton(addr, &(sin.sin_addr))) {
    /* Nope, so try to gethostbyname */
    if (!addr) {
      /* No addr at all, use host name from uname */
      if (uname(&uts) < 0) {
	perror("uname");
	exit(-1);
      }
    } else {
      strcpy(uts.nodename, addr);
    }
    
    if ((hp = gethostbyname(uts.nodename)) == NULL) {
      fprintf(stderr, "gethostbyname(%s): %s", uts.nodename, strerror(errno));
      exit(-1);
    }

    if (hp->h_addrtype != AF_INET) {
      fprintf(stderr, "Host is not on the internet!\n");
      exit(-1);
    }

    memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
  }

  if ((sd = socket(PF_INET, SOCK_DGRAM, 0)) <= 0) {
    printf("Could not make socket\n");
    exit(-1);
  }
  
  if (bind(sd, (struct sockaddr *)(&sin), sizeof(sin)) != 0) {
    if (errno == EADDRNOTAVAIL) {
      /* Network probably not up yet. */
      close(sd);	/* Error pointed out my Mike Cheponis */
      return ERR_NOREPLY;
    }
    perror("bind");
    exit(-1);
  }

  argp = 1;
#ifndef Solaris
  if (ioctl(sd, FIONBIO, (char *)&argp) != 0) {
    perror("ioctl FIONBIO");
    exit(-1);
  }
#endif
   
  return sd;
}

/* Bring net up with command */
int32 NetUp(char *cmd, char *addr, int32 port) {
  int res;

  res = system(cmd);
  /* 0x7e00 = perm denied
     0x7f00 = command not found
     256 * cmd if command runs */
  if (res != 0) return ERR_OTHER;

  return InitNetwork(addr, port);
}

/* Bring net down with command */
int NetDown(char *cmd, int32 oldsd) {
  int res;

  /* Close existing connection */
  if (oldsd >= 0) close(oldsd);

  res = system(cmd);
  if (res != 0) return ERR_OTHER;

  return ERR_NONE;
}

int NetGetAddr(struct sockaddr_in *sin, char *host, uint16 port) {
  struct hostent	*hp;

  if ((hp = gethostbyname(host)) == NULL) {
    fprintf(stderr, "gethostbyname(%s): %s", host, strerror(errno));
    return ERR_INRANGE;
  }

  if (hp->h_addrtype != AF_INET) {
    fprintf(stderr, "Host is not on the internet!\n");
    return ERR_INRANGE;
  }

  memset(sin, 0, sizeof(struct sockaddr_in));
  sin->sin_port = htons(port);
  sin->sin_family = AF_INET;
  memcpy(&(sin->sin_addr), hp->h_addr, hp->h_length);
  
  return ERR_NONE;
}

/* Listen for a data request.

   Listen on specified socket sd.  When one is received, validate the
   checksum, and, if successful, allocate memory and stuff it as a
   character array.  Return the remote sockaddr_in, if the sin pointer
   is not null, and block until something interesting happens if block
   is true. */
int NetListen(int sd, char **pktbuf, 
	      struct sockaddr_in *sin,
	      int block) {
  fd_set		fdset;
  struct timeval	timeout, *top;
  int32 		nfound;
  int32 		size;
  int32 		count;
  int32 		res;
  short 		pktsize;
  uint16		pkttype, cksumt, cksumc;

  if (sd < 0) return ERR_INRANGE;

  FD_ZERO(&fdset);
  FD_SET(sd, &fdset);
  timeout.tv_sec = timeout.tv_usec = 0;

  /* Wait for an incoming connection.
     If block is true, we wait indefinitely; otherwise, we
     return immediately because of zero timeout. */

  if (block) top = NULL; else top = &timeout;
  nfound = select(FD_SETSIZE, &fdset, 0, 0, top);

  if (nfound < 0) {
    if (errno == EWOULDBLOCK) return ERR_COMM_TMOUT;
    perror("select");
    return ERR_OTHER;
  }

  /* No connections, go back to main loop. */
  if (nfound == 0) return ERR_COMM_TMOUT;

#ifdef DEBUG
  fprintf(stderr, "Net port got a request!\n");
#endif

  if (!FD_ISSET(sd, &fdset)) {
    fprintf(stderr, "Confused condition -- FD not part of set.\n");
    return 0;
  }

  size = sizeof(*sin);
  count = recvfrom(sd, buffer, MAXBUFSIZE, 0,
		   (struct sockaddr *)sin, (socklen_t *)&size);
  if (count < 0) {
    /* Don't wait for it. */
    if (errno == EWOULDBLOCK) return ERR_COMM_TMOUT;
    perror("recvfrom");
    return ERR_OTHER;
  }

#ifdef HEXDUMP
  fprintf(stderr, "Received %d bytes from %s\n", count,
    inet_ntoa(sin->sin_addr));
  xd(stderr, buffer, count, FALSE);
#endif

  /* Mangle buffer as needed */
  if ((res = PktDecrypt(buffer, count)) < 0) {
    fprintf(stderr, "Packet decryption failed with %d.\n", (int)res);
    return res;
  }

  /* Verify length and CRC of packet.  In making these
     protocol-level sanity checks, we transform the relevant
     fields in the packet from network to host byte order
     as required.  Note, however, that the packet returned
     to the caller remains, in its entirety, in network
     byte order and even references to protocol-common
     fields, if made, must be converted by the code which
     invokes this function. */

  { uint16 rpkttype, rpktsize, rcrc;

    memcpy(&rpkttype, buffer, sizeof rpkttype);
    memcpy(&rpktsize, buffer + 2, sizeof rpktsize);
    memcpy(&rcrc, buffer + (count - 2), sizeof rcrc);
    pktsize = ntohs(rpktsize);
    if (pktsize != count) {
#ifdef DEBUG
        fprintf(stderr, "** Bad packet length: pktsize = %d, count = %d.\n",
	    pktsize, count);
#endif
	return ERR_PKT_BADLEN;
    }
    cksumt = ntohs(rcrc);
    cksumc = BlockCRC16((byte *) buffer, count - 2);
    if (cksumc != cksumt) {
#ifdef DEBUG
        fprintf(stderr, "** Bad packet CRC: packet = %04X, computed = %04X.\n",
	    cksumt, cksumc);
#endif
	return ERR_PKT_CKSUM;
    }
    pkttype = ntohs(rpkttype);
  }

#ifdef DEBUG
  fprintf(stderr, "Recv packet type %04x, %d bytes (hdr), cks = %04x\n",
	  pkttype, pktsize, cksumt);
#endif /* DEBUG */
  
  /* Hand back the packet */
  *pktbuf = (char *)malloc(count);
  memcpy(*pktbuf, buffer, count);

  return ERR_NONE;
}

/* Send a data packet.

   Checksum the packet, create a socket, and send it to the specified
   port of the specified host.	Length of packet to be extracted from
   packet header. */

  /* The entire contents of the packet passed to NetTalk must
     be in network byte order.	Fields within the packet used
     to append the CRC, determine the number of bytes to send,
     etc. are converted to host byte order as needed. */

int NetTalk(struct sockaddr_in *sin, char *pkt, int gripe) {
  uint16		pktsize, cksum;
  static struct protoent *pp = NULL;
  int			i, out_sock;

  memcpy(&pktsize, pkt + 2, 2);
  pktsize = ntohs(pktsize);
#ifdef DEBUG
  if (pktsize > 1500) {
    fprintf(stderr, "Bogus packet size of %ud bytes.  Suspect byte alignment bug.\n", pktsize);
    abort();
  }
#endif
  cksum = htons(BlockCRC16((byte *) pkt, pktsize - sizeof(uint16)));
  memcpy(pkt+pktsize-sizeof(uint16), &cksum, sizeof(uint16));

  if (pp == NULL) {		      /* Only get protocol code the first time */
    if ((pp = getprotobyname("udp")) == NULL) {
      perror("getprotobyname");
      return ERR_OTHER;
    }
  }

  if ((out_sock = socket(AF_INET, SOCK_DGRAM, pp->p_proto)) < 0) {
    perror("socket");
    return ERR_OTHER;
  }
	
  i = sendto(out_sock, pkt, pktsize, 
	     0, (struct sockaddr *)sin,
	     sizeof(struct sockaddr));
  if (i == -1) {
    /* We only report on certain errors if told to "gripe", because
       it is a normal occurence if in PERM mode with a variably
       available net connection. */
    if ((errno != ENETUNREACH && errno != EHOSTUNREACH) || gripe) perror("sendto");
    close(out_sock);
    return ERR_OTHER;
  }

#ifdef HEXDUMP
  fprintf(stderr, "Sent %d bytes to %s\n", pktsize, inet_ntoa(sin->sin_addr));
  xd(stderr, pkt, pktsize, FALSE);
#endif

  i = close(out_sock);
  if (i < 0) {
    perror("close");
  }
  
  return ERR_NONE;
}