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