1 /* Tornado - Two player weather action game
2  *
3  * Copyright (C) 2000  Rene Puls (kianga@claws-and-paws.com)
4  * network.c
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 
22 #include <stdio.h>
23 #include <assert.h>
24 #include <netdb.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netinet/tcp.h>
32 #include <arpa/inet.h>
33 
34 /* local variables */
35 static int socketfd;		       /* active socket file descriptor */
36 static int const one = 1; 	       /* for the stupid setsockopt(7) function */
37 static char *recvbuf = NULL;       /* our receive buffer */
38 static char remote_name[256];	   /* remote host name */
39 
40 /* description:
41  *    opens a listening socket and waits for a connection
42  *
43  * arguments:
44  *    port -- the port number on which we should listen
45  *
46  * returns:
47  *    0 -- success
48  *   -1 -- failure
49  */
50 
network_listen(int port)51 int network_listen(int port)
52 {
53   int listenfd;		       /* listening file descriptor */
54   int result;			       /* used to hold result codes of
55 					* various functions */
56 
57   socklen_t addr_len;
58   struct sockaddr_in my_address;      /* our own address (ip+port) */
59   struct sockaddr_in remote_address;  /* remote address */
60   struct hostent *rhostent;
61 
62   /* fill in the port */
63   my_address.sin_family = AF_INET;    /* TCP/IP family */
64   my_address.sin_addr.s_addr = htonl(INADDR_ANY); /* (all interfaces) */
65   my_address.sin_port = htons(port);  /* port to listen on */
66 
67   /* create a socket */
68   listenfd = socket(AF_INET, SOCK_STREAM, 0);
69   if (listenfd < 0)
70     return -1;
71 
72   /* set SO_REUSEADDR so that we can bind to the socket even if there is
73    * still a TIME_WAIT connection on this port (maybe from a previous
74    * instance of this program) */
75   setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
76 
77   /* bind the socket on a port */
78   result = bind(listenfd, (struct sockaddr *) &my_address, sizeof(my_address));
79   if (result < 0)
80     return -1;
81 
82   /* wait for a connection */
83   listen(listenfd, 1);
84 
85   addr_len = sizeof(struct sockaddr_in);
86 
87   /* accept the connection */
88   socketfd = accept(listenfd, (struct sockaddr *) &remote_address, &addr_len);
89   if (socketfd < 0)
90     return -1;
91 
92   /* try to resolve the remote address */
93   rhostent = gethostbyaddr((char *)&remote_address.sin_addr,
94 			   sizeof(struct in_addr), AF_INET);
95   if (rhostent != NULL)
96     strcpy(remote_name, rhostent->h_name);
97   else /* if we cannot resolve it, use the IP address */
98     strcpy(remote_name, inet_ntoa(remote_address.sin_addr));
99 
100   /* close the listening socket */
101   if (close(listenfd) != 0)
102     return -1;
103 
104   return 0;
105 }
106 
107 /* description:
108  *    connects to a remote host
109  *
110  * arguments:
111  *    host -- a remote address (host name or IP)
112  *    port -- the port to connect to
113  *
114  * returns:
115  *    0 -- success
116  *   -1 -- failure
117  */
118 
network_connect(char * host,int port)119 int network_connect(char *host, int port)
120 {
121   int result;			       /* used to hold result codes of
122 					  various functions */
123   struct sockaddr_in address;      /* our own address (ip+port) */
124   struct hostent *h;
125 
126   /* resolve the host name */
127   h = gethostbyname(host);
128   if (h == NULL)
129     return -2;						   /* exitstat -2 is checked seperately in main.c */
130 
131   /* fill in the port */
132   address.sin_family = AF_INET;    /* TCP/IP family */
133   memcpy((char *) &address.sin_addr.s_addr, h->h_addr_list[0], h->h_length);
134   address.sin_port = htons(port);  /* port to listen on */
135 
136   /* create the socket */
137   socketfd = socket(AF_INET, SOCK_STREAM, 0);
138   if (socketfd < 0)
139     return -1;
140 
141   /* connect to the remote host */
142   result = connect(socketfd, (struct sockaddr *) &address, sizeof(address));
143   if (result < 0)
144     return -1;
145 
146   /* try to resolve the remote address */
147   h = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
148   if (h != NULL) {
149     strcpy(remote_name, h->h_name);
150   } else {
151     /* if not resolvable, use the IP dotted notation */
152     strcpy(remote_name, inet_ntoa(address.sin_addr));
153   }
154 
155   return 0;
156 }
157 
158 /* description:
159  *    closes the active connection
160  *
161  * returns:
162  *    0 -- success
163  *   -1 -- failure
164  */
165 
network_close(void)166 int network_close(void)
167 {
168   if (close(socketfd) != 0)
169     return -1;
170   else
171     return 0;
172 }
173 
174 /* description:
175  *    returns the host name or IP address of the remote host
176  *
177  * arguments:
178  *    none
179  *
180  * returns:
181  *    a pointer to the remote host name
182  *    this is a statically allocated buffer - it must not be free()'d
183  */
184 
network_get_remote_name(void)185 char const *network_get_remote_name(void)
186 {
187   return remote_name;
188 }
189 
190 
191 
192 /* description:
193  *    reads data from the active connection
194  *
195  * arguments:
196  *    buf -- buffer to hold a null-terminated string containing the received
197  *           data
198  *    size -- size of the above buffer
199  *
200  * returns:
201  *    0 -- success
202  *   -1 -- failure
203  */
204 
network_read(char * buf,int size)205 int network_read(char *buf, int size)
206 {
207   char tmpbuf[1024];		       /* temporary receive buffer */
208   char *eol_tmp;		       /* pointer to the eol of the first string */
209   int result;
210   int newlen;
211 
212   if (recvbuf == NULL) {	       /* allocate recvbuf the first time it is used */
213     recvbuf = (char *) malloc(1);
214     recvbuf[0] = '\0';
215   }
216 
217   /* now loop until at least one complete line (\n) is in the buffer */
218   while (memchr(recvbuf, '\n', strlen(recvbuf)) == NULL) {
219 
220     /* read data from the socket */
221     result = read(socketfd, tmpbuf, sizeof(tmpbuf));
222     if (result < 0) /* abort immediately if read failed */
223       return -1;
224 
225     /* ASSERTION: received data must not contain any \0 characters */
226     assert(memchr(tmpbuf, '\0', result) == NULL);
227 
228     /* compute the new size of the buffer */
229     newlen = strlen(recvbuf)+result;
230 
231     /* increase the size of the recvbuf so that the new data fits in */
232     recvbuf = (char *) realloc(recvbuf, newlen+1);
233 
234     /* append the new data to the buffer */
235     memcpy(&recvbuf[strlen(recvbuf)], tmpbuf, result);
236 
237     /* terminate the buffer with \0 */
238     recvbuf[newlen] = '\0';
239   }
240 
241   /* now read the first line from the buffer and remove it afterwards: */
242 
243   /* save the position of the end-of-line char */
244   eol_tmp = memchr(recvbuf, '\n', strlen(recvbuf));
245 
246   /* cut off the first part and let eol_tmp point to the following string */
247   eol_tmp[0] = '\0';
248   ++eol_tmp;
249 
250   /* okay. copy the first string into user supplied buffer */
251   strncpy(buf, recvbuf, size);
252 
253   /* now move the new string plus \0 to the beginning of the buffer */
254   memmove(recvbuf, eol_tmp, strlen(eol_tmp)+1);
255 
256   /* finally, shrink the buffer to fit just the new string */
257   recvbuf = (char *) realloc(recvbuf, strlen(recvbuf)+1);
258 
259   return 0;
260 }
261 
262 /* description:
263  *    reads an char from the network
264  *
265  * arguments:
266  *    ch -- pointer to a char variable
267  *
268  * returns:
269  *    0 -- success
270  *   -1 -- failure
271  *
272  * notes:
273  *   this actually reads a string and returns just the first character.
274  */
275 
network_readch(char * ch)276 int network_readch(char *ch)
277 {
278   char buf[2];
279 
280   /* read a line from the network and convert it to an integer */
281   network_read(buf, sizeof(buf));
282   *ch = buf[0];
283 
284   return 0;
285 }
286 
287 /* description:
288  *    reads an integer from the network
289  *
290  * arguments:
291  *    i -- pointer to an integer variable
292  *
293  * returns:
294  *    0 -- success
295  *   -1 -- failure
296  */
297 
network_readint(int * i)298 int network_readint(int *i)
299 {
300   char buf[20];
301 
302   /* read a line from the network and convert it to an integer */
303   network_read(buf, sizeof(buf));
304   *i = atoi(buf);
305 
306   return 0;
307 }
308 
309 /* description:
310  *    sends data over the active connection
311  *
312  * returns:
313  *    0 -- success
314  *   -1 -- failure
315  */
316 
network_write(char * buf)317 int network_write(char *buf)
318 {
319   char *tmpbuf;		       /* temporary send buffer */
320   int result;
321 
322   /* allocate a temporary buffer - we need to append a \n to
323    * the end of the data. */
324   tmpbuf = (char *) malloc(strlen(buf)+2);
325   strcpy(tmpbuf, buf);
326   strcat(tmpbuf, "\n");
327 
328   /* send the line */
329   result = write(socketfd, tmpbuf, strlen(tmpbuf));
330 
331   /* deallocate the temp buffer again */
332   free(tmpbuf);
333 
334   if (result < 0)
335     return -1;
336   else
337     return 0;
338 }
339 
340 /* description:
341  *    sends a character over the active connection
342  *
343  * arguments:
344  *    ch -- a single character
345  *
346  * returns:
347  *    0 -- success
348  *   -1 -- failure
349  */
350 
network_writech(char ch)351 int network_writech(char ch)
352 {
353   char *tmpbuf;
354   int result;
355 
356   /* convert the character to a short string with \n */
357   tmpbuf = (char *) malloc(2);
358   tmpbuf[0] = ch;
359   tmpbuf[1] = '\n';
360 
361   /* send it */
362   result = write(socketfd, tmpbuf, 2);
363 
364   free(tmpbuf);
365 
366   if (result < 0)
367     return -1;
368   else
369     return 0;
370 }
371 
372 /* description:
373  *    write an integer to the network
374  *
375  * arguments:
376  *    i -- an integer
377  *
378  * returns:
379  *    0 -- success
380  *   -1 -- failure
381  */
382 
network_writeint(int i)383 int network_writeint(int i)
384 {
385   char tmpbuf[20];
386   int result;
387 
388   sprintf(tmpbuf, "%d\n", i);
389 
390   result = write(socketfd, tmpbuf, strlen(tmpbuf));
391 
392   if (result < 0)
393     return -1;
394   else
395     return 0;
396 }
397 
398