Release s3d 0.2.2
[s3d.git] / server / tcp.c
blob1cd36dfb06f1eb0ec726317090a79a4c112bf6c1
1 /*
2 * tcp.c
4 * Copyright (C) 2004-2011 Simon Wunderlich <dotslash@packetmixer.de>
6 * This file is part of s3d, a 3d network display server.
7 * See http://s3d.berlios.de/ for more updates.
9 * s3d is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * s3d is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with s3d; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "global.h"
25 #ifdef TCP
26 #include <errno.h> /* errno */
27 #include <string.h> /* memset() */
28 #ifdef WIN32 /* sohn wars */
29 #include <winsock2.h>
30 #else /* sohn wars */
31 #include <sys/types.h> /* fd_set, FD*, socket, accept ... */
32 #include <sys/socket.h> /* socket, accept ... */
33 #include <sys/select.h> /* fd_set,FD* */
34 #include <sys/time.h> /* fd_set,FD* */
35 #include <netinet/in.h> /* ntohs(),htons(),htonl(),ntohl() */
36 #include <arpa/inet.h> /* network */
37 #endif /* sohn wars */
38 #include <time.h> /* select() timeval things */
39 #include <fcntl.h> /* fcntl(),F_SETOWN */
40 #ifndef F_SETOWN /* somehow it is not set with -ansi */
41 #define F_SETOWN 8
42 #endif
43 #include <unistd.h> /* read(),write(),getpid(),close() */
44 #include <stdlib.h> /* malloc(),free() */
45 #include <stdint.h>
47 static int tcp_sockid;
49 int tcp_init(void)
51 int yes = 1;
52 struct sockaddr_in my_addr;
53 s3dprintf(LOW, "server: creating socket");
54 #ifdef WIN32 /* sohn wars */
55 WSADATA datainfo;
56 if (WSAStartup(257, &datainfo) != 0)
57 errnf("startup()", 0);
58 #endif /* auch sohn */
59 if ((tcp_sockid = socket(AF_INET, SOCK_STREAM, 0)) < 0)
60 errnf("socket()", errno);
62 s3dprintf(LOW, "server: binding my local socket");
63 /* allow addresses to be reused */
64 /* this seems to have something to do with servers using one port */
65 if (setsockopt(tcp_sockid, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
66 errn("setsockopt(...,SO_REUSEADDR...)", errno);
67 memset((char *)&my_addr, 0, sizeof(my_addr));
68 my_addr.sin_family = AF_INET;
69 my_addr.sin_port = htons(S3D_PORT);
70 my_addr.sin_addr.s_addr = htons(INADDR_ANY);
71 if (bind(tcp_sockid, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0)
72 errnf("bind()", errno);
73 if (listen(tcp_sockid, 5) < 0)
74 errnf("listen()", errno);
75 #ifdef SIGS
76 if (fcntl(tcp_sockid, F_SETFL, O_ASYNC | O_NONBLOCK) < 0)
77 errnf("fcntl()", errno);
78 if (fcntl(tcp_sockid, F_SETOWN, getpid()) < 0)
79 errnf("fcntl()", errno);
80 #endif
81 return 0;
85 int tcp_quit(void)
87 close(tcp_sockid);
88 #ifdef WIN32
89 WSACleanup();
90 #endif
91 return 0;
94 /* watches the port for new connections */
95 int tcp_pollport(void)
97 fd_set fs_port; /* filedescriptor set for listening port(s) */
98 int newsd; /* new socket descriptor */
99 struct timeval tv; /* time structure */
100 struct t_process *new_p; /* pointer to new process */
101 struct sockaddr client_addr; /* new client's address */
102 socklen_t clilen = sizeof(client_addr); /* length of client's address */
104 FD_ZERO(&fs_port);
105 FD_SET(tcp_sockid, &fs_port);
106 select_again:
107 tv.tv_sec = tv.tv_usec = 0;
108 if (select(FD_SETSIZE, &fs_port, NULL, NULL, &tv) < 0) {
109 if (errno == EINTR) { /* interruption by some evil signal, just do again :) */
110 errn("tcp_pollport():select()", errno);
111 goto select_again; /* oh no, a goto!! that's evil */
112 } else
113 errn("tcp_pollport():select()", errno);
114 } else if (FD_ISSET(tcp_sockid, &fs_port)) { /* redundant, I guess */
115 s3dprintf(HIGH, "select(): new connection!!");
116 if ((newsd = accept(tcp_sockid, (struct sockaddr *)&client_addr, &clilen)) < 0)
117 errn("accept()", errno);
118 else {
119 #ifdef SIGS
120 if (fcntl(newsd, F_SETFL, O_ASYNC) < 0)
121 errnf("fcntl()", errno);
122 if (fcntl(newsd, F_SETOWN, getpid()) < 0)
123 errnf("fcntl()", errno);
124 #endif
125 new_p = process_add();
126 new_p->con_type = CON_TCP;
127 new_p->sockid = newsd;
128 s3dprintf(HIGH, "registered new connection %d as pid %d", new_p->sockid, new_p->id);
131 return 0;
134 /* this is about looking for new data on the sockets */
135 /* returns 1 when there was new data. */
136 int tcp_pollproc(void)
138 fd_set fs_proc; /* filedescriptor set for listening port(s) */
139 struct timeval tv; /* time structure */
140 struct t_process *p;
141 int found = 0;
142 int i, unfinished, n, off;
143 off = 0;
144 do {
145 FD_ZERO(&fs_proc);
146 unfinished = 0;
147 n = 0;
148 for (i = off; i < procs_n; i++) {
149 p = &procs_p[i];
150 if (p->con_type == CON_TCP) {
151 FD_SET(p->sockid, &fs_proc);
152 n++;
153 if (n >= FD_SETSIZE) { /* don't overflow the setsize! */
154 off = i;
155 unfinished = 1;
156 break;
160 /* maybe having a global fd_set for all the processes would have been better */
161 /* than generating them new in every poll. to be optimized... */
162 select_again_poll:
163 tv.tv_sec = tv.tv_usec = 0;
164 if (select(FD_SETSIZE, &fs_proc, NULL, NULL, &tv) == -1) {
165 if (errno == EINTR) {
166 errn("tcp_pollproc():select()", errno);
167 goto select_again_poll;
168 } else {
169 errn("tcp_pollproc():select()", errno);
171 } else {
172 /* data is available */
173 for (i = 0; i < procs_n; i++) {
174 p = &procs_p[i];
175 if (p->con_type == CON_TCP) {
176 if (FD_ISSET(p->sockid, &fs_proc)) {
177 FD_CLR(p->sockid, &fs_proc); /* clear it from the fd */
178 tcp_prot_com_in(p);
179 found = 1;
184 } while (unfinished);
185 return found;
188 /* read some data from the line, pushes it into the buffer and calls prot_com_in */
189 int tcp_prot_com_in(struct t_process *p)
191 uint16_t length;
192 if (3 == tcp_readn(p->sockid, ibuf, 3)) {
193 length = ntohs(*((uint16_t *) ((uint8_t *) ibuf + 1)));
194 s3dprintf(VLOW, "command %d, length %d", ibuf[0], length);
195 if (length > 0) {
196 tcp_readn(p->sockid, ibuf + sizeof(int_least32_t), length); /* uint16_t is limited to 65536, so */
197 /* length can't be bigger than that ... lucky */
199 prot_com_in(p, ibuf);
200 } else {
201 s3dprintf(LOW, "tcp_prot_com_in():n_readn():fd seems to be dead (pid %d, sock %d)", p->id, p->sockid);
202 process_del(p->id);
204 return 0;
207 /* shamelessly ripped from simple ftp server */
208 int tcp_readn(int sock, uint8_t * str, int s)
210 int no_left, no_read;
211 no_left = s;
212 while (no_left > 0) {
213 no_read = read(sock, str, no_left);
214 if (no_read < 0) {
215 errn("read()", errno);
216 return no_read;
218 if (no_read == 0)
219 break;
220 no_left -= no_read;
221 str += no_read;
223 return s - no_left;
226 int tcp_writen(int sock, uint8_t * str, int s)
228 int no_left, no_written;
229 no_left = s;
230 while (no_left > 0) {
231 no_written = write(sock, str, no_left);
232 if (no_written <= 0) {
233 errn("write()", errno);
234 return no_written;
236 no_left -= no_written;
237 str += no_written;
239 return s - no_left;
242 int tcp_remove(int sock)
244 return close(sock);
246 #endif