... | ... |
@@ -241,7 +241,7 @@ static inline int init_su( union sockaddr_union* su, |
241 | 241 |
|
242 | 242 |
|
243 | 243 |
|
244 |
-/* inits a struct sockaddr_union from a struct hostent, an address index int |
|
244 |
+/* inits a struct sockaddr_union from a struct hostent, an address index in |
|
245 | 245 |
* the hostent structure and a port no. |
246 | 246 |
* WARNING: no index overflow checks! |
247 | 247 |
* returns 0 if ok, -1 on error (unknown address family) */ |
... | ... |
@@ -33,6 +33,13 @@ |
33 | 33 |
|
34 | 34 |
|
35 | 35 |
#define TCP_BUF_SIZE 65535 |
36 |
+#define TCP_CON_TIMEOUT 60 /* in seconds */ |
|
37 |
+#define TCP_CHILD_TIMEOUT 5 /* after 5 seconds, the child "returns" |
|
38 |
+ the connection to the tcp master process */ |
|
39 |
+#define TCP_MAIN_SELECT_TIMEOUT 5 /* how often "tcp main" checks for timeout*/ |
|
40 |
+#define TCP_CHILD_SELECT_TIMEOUT 2 /* the same as above but for children */ |
|
41 |
+ |
|
42 |
+ |
|
36 | 43 |
enum { TCP_REQ_INIT, TCP_REQ_OK, TCP_READ_ERROR, TCP_REQ_OVERRUN, |
37 | 44 |
TCP_REQ_BAD_LEN }; |
38 | 45 |
enum { H_SKIP, H_LF, H_LFCR, H_BODY, H_STARTWS, |
... | ... |
@@ -44,7 +51,6 @@ enum { H_SKIP, H_LF, H_LFCR, H_BODY, H_STARTWS, |
44 | 51 |
|
45 | 52 |
struct tcp_req{ |
46 | 53 |
struct tcp_req* next; |
47 |
- int fd; |
|
48 | 54 |
/* sockaddr ? */ |
49 | 55 |
char buf[TCP_BUF_SIZE]; /* bytes read so far*/ |
50 | 56 |
char* pos; /* current position in buf */ |
... | ... |
@@ -59,17 +65,51 @@ struct tcp_req{ |
59 | 65 |
}; |
60 | 66 |
|
61 | 67 |
|
62 |
-#define init_tcp_req( r, f) \ |
|
68 |
+ |
|
69 |
+ |
|
70 |
+struct tcp_connection{ |
|
71 |
+ int s; /*socket, used by "tcp main" */ |
|
72 |
+ int fd; /* used only by "children" */ |
|
73 |
+ struct ip_addr ip; /* peer ip */ |
|
74 |
+ int sock_idx; /* receiving socket index in the tcp_info array */ |
|
75 |
+ union sockaddr_union su; |
|
76 |
+ struct tcp_req req; /* request data */ |
|
77 |
+ int refcnt; |
|
78 |
+ int timeout; /* connection timeout, after this it will be removed*/ |
|
79 |
+ struct tcp_connection* next; |
|
80 |
+ struct tcp_connection* prev; |
|
81 |
+}; |
|
82 |
+ |
|
83 |
+ |
|
84 |
+ |
|
85 |
+ |
|
86 |
+#define init_tcp_req( r) \ |
|
63 | 87 |
do{ \ |
64 | 88 |
memset( (r), 0, sizeof(struct tcp_req)); \ |
65 |
- (r)->fd=(f); \ |
|
66 | 89 |
(r)->parsed=(r)->pos=(r)->buf; \ |
67 | 90 |
(r)->error=TCP_REQ_OK;\ |
68 | 91 |
(r)->state=H_STARTWS; \ |
69 | 92 |
}while(0) |
70 | 93 |
|
71 | 94 |
|
95 |
+/* add a tcpconn to a list*/ |
|
96 |
+#define tcpconn_listadd(head, c) \ |
|
97 |
+ do{ \ |
|
98 |
+ /* add it at the begining of the list*/ \ |
|
99 |
+ (c)->next=(head); \ |
|
100 |
+ (c)->prev=0; \ |
|
101 |
+ if ((head)) (head)->prev=(c); \ |
|
102 |
+ (head)=(c); \ |
|
103 |
+ } while(0) |
|
104 |
+ |
|
72 | 105 |
|
106 |
+/* remove a tcpconn from a list*/ |
|
107 |
+#define tcpconn_listrm(head, c) \ |
|
108 |
+ do{ \ |
|
109 |
+ if ((head)==(c)) (head)=(c)->next; \ |
|
110 |
+ if ((c)->next) (c)->next->prev=(c)->prev; \ |
|
111 |
+ if ((c)->prev) (c)->prev->next=(c)->next; \ |
|
112 |
+ }while(0) |
|
73 | 113 |
|
74 | 114 |
|
75 | 115 |
|
... | ... |
@@ -28,6 +28,11 @@ |
28 | 28 |
|
29 | 29 |
#ifdef USE_TCP |
30 | 30 |
|
31 |
+ |
|
32 |
+#ifndef SHM_MEM |
|
33 |
+#error "shared memory support needed (add -DSHM_MEM to Makefile.defs)" |
|
34 |
+#endif |
|
35 |
+ |
|
31 | 36 |
#include <sys/select.h> |
32 | 37 |
|
33 | 38 |
#include <sys/time.h> |
... | ... |
@@ -43,10 +48,12 @@ |
43 | 48 |
|
44 | 49 |
#include "ip_addr.h" |
45 | 50 |
#include "pass_fd.h" |
51 |
+#include "tcp_conn.h" |
|
46 | 52 |
#include "globals.h" |
47 | 53 |
#include "mem/mem.h" |
48 | 54 |
|
49 | 55 |
|
56 |
+ |
|
50 | 57 |
#define local_malloc pkg_malloc |
51 | 58 |
#define local_free pkg_free |
52 | 59 |
|
... | ... |
@@ -62,14 +69,6 @@ struct tcp_child{ |
62 | 69 |
|
63 | 70 |
enum { CONN_OK, CONN_ERROR }; |
64 | 71 |
|
65 |
-struct tcp_connection{ |
|
66 |
- int s; /*socket */ |
|
67 |
- struct ip_addr ip; |
|
68 |
- union sockaddr_union su; |
|
69 |
- int refcnt; |
|
70 |
- struct tcp_connection* next; |
|
71 |
- struct tcp_connection* prev; |
|
72 |
-}; |
|
73 | 72 |
|
74 | 73 |
|
75 | 74 |
|
... | ... |
@@ -83,7 +82,7 @@ struct tcp_connection* tcpconn_add(int sock, union sockaddr_union* su) |
83 | 82 |
struct tcp_connection *c; |
84 | 83 |
|
85 | 84 |
|
86 |
- c=(struct tcp_connection*)local_malloc(sizeof(struct tcp_connection)); |
|
85 |
+ c=(struct tcp_connection*)shm_malloc(sizeof(struct tcp_connection)); |
|
87 | 86 |
if (c==0){ |
88 | 87 |
LOG(L_ERR, "ERROR: tcpconn_add: mem. allocation failure\n"); |
89 | 88 |
goto error; |
... | ... |
@@ -92,12 +91,11 @@ struct tcp_connection* tcpconn_add(int sock, union sockaddr_union* su) |
92 | 91 |
c->su=*su; |
93 | 92 |
c->refcnt=0; |
94 | 93 |
su2ip_addr(&c->ip, su); |
94 |
+ init_tcp_req(&c->req); |
|
95 |
+ c->timeout=get_ticks()+TCP_CON_TIMEOUT; |
|
95 | 96 |
|
96 | 97 |
/* add it at the begining of the list*/ |
97 |
- c->next=conn_list; |
|
98 |
- c->prev=0; |
|
99 |
- if (conn_list) conn_list->prev=c; |
|
100 |
- conn_list=c; |
|
98 |
+ tcpconn_listadd(conn_list, c); |
|
101 | 99 |
return c; |
102 | 100 |
|
103 | 101 |
error: |
... | ... |
@@ -108,11 +106,29 @@ error: |
108 | 106 |
|
109 | 107 |
void tcpconn_rm(struct tcp_connection* c) |
110 | 108 |
{ |
109 |
+ tcpconn_listrm(conn_list, c); |
|
110 |
+ shm_free(c); |
|
111 |
+} |
|
112 |
+ |
|
113 |
+ |
|
114 |
+/* very ineficient for now, use hashtable some day - FIXME*/ |
|
115 |
+void tcpconn_timeout() |
|
116 |
+{ |
|
117 |
+ struct tcp_connection *c, *next; |
|
118 |
+ int jiffies;; |
|
111 | 119 |
|
112 |
- if (conn_list==c) conn_list=c->next; |
|
113 |
- if (c->next) c->next->prev=c->prev; |
|
114 |
- if (c->prev) c->prev->next=c->next; |
|
115 |
- local_free(c); |
|
120 |
+ |
|
121 |
+ jiffies=get_ticks(); |
|
122 |
+ c=conn_list; |
|
123 |
+ while(c){ |
|
124 |
+ next=c->next; |
|
125 |
+ if ((c->refcnt==0) && (jiffies<c->timeout)) { |
|
126 |
+ DBG("tcpconn_timeout: timeout for %p (%d < %d)\n", |
|
127 |
+ c, jiffies, c->timeout); |
|
128 |
+ tcpconn_rm(c); |
|
129 |
+ } |
|
130 |
+ c=next; |
|
131 |
+ } |
|
116 | 132 |
} |
117 | 133 |
|
118 | 134 |
|
... | ... |
@@ -195,6 +211,7 @@ void tcp_main_loop() |
195 | 211 |
int state; |
196 | 212 |
int bytes; |
197 | 213 |
socklen_t su_len; |
214 |
+ struct timeval timeout; |
|
198 | 215 |
|
199 | 216 |
/*init */ |
200 | 217 |
maxfd=0; |
... | ... |
@@ -219,9 +236,14 @@ void tcp_main_loop() |
219 | 236 |
|
220 | 237 |
while(1){ |
221 | 238 |
sel_set=master_set; |
222 |
- n=select(maxfd+1, &sel_set, 0 ,0 , 0); |
|
239 |
+ timeout->tv_sec=TCP_MAIN_SELECT_TIMEOUT; |
|
240 |
+ timeout->tv_usec=0; |
|
241 |
+ n=select(maxfd+1, &sel_set, 0 ,0 , &timeout); |
|
223 | 242 |
if (n<0){ |
243 |
+ if (errno==EINTR) continue; /* just a signal */ |
|
224 | 244 |
/* errors */ |
245 |
+ LOG(L_ERR, "ERROR: tcp_main_loop: select:(%d) %s\n", errno, |
|
246 |
+ strerror(errno)); |
|
225 | 247 |
} |
226 | 248 |
|
227 | 249 |
for (r=0; r<sock_no && n; r++){ |
... | ... |
@@ -301,6 +323,8 @@ read_again: |
301 | 323 |
if (tcpconn->refcnt==0){ |
302 | 324 |
FD_SET(tcpconn->s, &master_set); |
303 | 325 |
if (maxfd<tcpconn->s) maxfd=tcpconn->s; |
326 |
+ /* update the timeout*/ |
|
327 |
+ tcpconn->timeout=get_ticks()+TCP_CON_TIMEOUT; |
|
304 | 328 |
} |
305 | 329 |
}else{ |
306 | 330 |
/*error, we should destroy it */ |
... | ... |
@@ -317,6 +341,9 @@ read_again: |
317 | 341 |
} |
318 | 342 |
} |
319 | 343 |
} |
344 |
+ |
|
345 |
+ /* remove old connections */ |
|
346 |
+ tcpconn_timeout(); |
|
320 | 347 |
|
321 | 348 |
} |
322 | 349 |
} |
... | ... |
@@ -52,7 +52,7 @@ |
52 | 52 |
/* reads next available bytes |
53 | 53 |
* return number of bytes read, 0 on EOF or -1 on error, |
54 | 54 |
* sets also r->error */ |
55 |
-int tcp_read(struct tcp_req *r) |
|
55 |
+int tcp_read(struct tcp_req *r, int fd) |
|
56 | 56 |
{ |
57 | 57 |
int bytes_free, bytes_read; |
58 | 58 |
|
... | ... |
@@ -64,7 +64,7 @@ int tcp_read(struct tcp_req *r) |
64 | 64 |
return -1; |
65 | 65 |
} |
66 | 66 |
again: |
67 |
- bytes_read=read(r->fd, r->pos, bytes_free); |
|
67 |
+ bytes_read=read(fd, r->pos, bytes_free); |
|
68 | 68 |
|
69 | 69 |
if(bytes_read==-1){ |
70 | 70 |
if (errno == EWOULDBLOCK || errno == EAGAIN){ |
... | ... |
@@ -92,7 +92,7 @@ again: |
92 | 92 |
* when either r->body!=0 or r->state==H_BODY => |
93 | 93 |
* all headers have been read. It should be called in a while loop. |
94 | 94 |
* returns < 0 if error or 0 if EOF */ |
95 |
-int tcp_read_headers(struct tcp_req *r) |
|
95 |
+int tcp_read_headers(struct tcp_req *r, int fd) |
|
96 | 96 |
{ |
97 | 97 |
int bytes, remaining; |
98 | 98 |
char *p; |
... | ... |
@@ -133,7 +133,7 @@ int tcp_read_headers(struct tcp_req *r) |
133 | 133 |
|
134 | 134 |
|
135 | 135 |
|
136 |
- bytes=tcp_read(r); |
|
136 |
+ bytes=tcp_read(r, fd); |
|
137 | 137 |
if (bytes<=0) return bytes; |
138 | 138 |
p=r->parsed; |
139 | 139 |
|
... | ... |
@@ -313,52 +313,86 @@ skip: |
313 | 313 |
|
314 | 314 |
void tcp_receive_loop(int unix_sock) |
315 | 315 |
{ |
316 |
- struct tcp_req req; |
|
316 |
+ struct tcp_req* req; |
|
317 |
+ struct tcp_connection* list; /* list with connections in use */ |
|
318 |
+ struct tcp_connection* con; |
|
317 | 319 |
int bytes; |
318 | 320 |
long size; |
319 | 321 |
int n; |
320 |
- long id; |
|
322 |
+ int nfds; |
|
321 | 323 |
int s; |
322 | 324 |
long state; |
323 | 325 |
long response[2]; |
326 |
+ fd_set master_set; |
|
327 |
+ fd_set sel_set; |
|
328 |
+ int maxfd; |
|
329 |
+ struct timeval timeout; |
|
324 | 330 |
|
325 | 331 |
|
326 |
- |
|
332 |
+ /* init */ |
|
333 |
+ list=con=0; |
|
334 |
+ FD_ZERO(&master_set); |
|
335 |
+ FD_SET(unix_sock, &master_set); |
|
336 |
+ maxfd=unix_sock; |
|
327 | 337 |
|
328 | 338 |
/* listen on the unix socket for the fd */ |
329 | 339 |
for(;;){ |
330 |
- n=receive_fd(unix_sock, &id, sizeof(id), &s); |
|
331 |
- if (n<0){ |
|
332 |
- if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR){ |
|
333 |
- continue; |
|
334 |
- }else{ |
|
335 |
- fprintf(stderr, "ERROR: tcp_receive_loop: read_fd: %s\n", |
|
340 |
+ timeout->tv_sec=TCP_CHILD_SELECT_TIMEOUT; |
|
341 |
+ timeout->tv_usec=0; |
|
342 |
+ sel_set=master_set; |
|
343 |
+ nfds=select(maxfd+1, &sel_set, 0 , 0 , &timeout); |
|
344 |
+ if (nfds<0){ |
|
345 |
+ if (errno==EINTR) continue; /* just a signal */ |
|
346 |
+ /* errors */ |
|
347 |
+ LOG(L_ERR, "ERROR: tcp_receive_loop: select:(%d) %s\n", errno, |
|
348 |
+ strerror(errno)); |
|
349 |
+ continue; |
|
350 |
+ } |
|
351 |
+ if (FD_ISSET(unix_sock, &sel_set)){ |
|
352 |
+ nfds--; |
|
353 |
+ /* a new conn from "main" */ |
|
354 |
+ n=receive_fd(unix_sock, &con, sizeof(con), &s); |
|
355 |
+ if (n<0){ |
|
356 |
+ if (errno == EWOULDBLOCK || errno == EAGAIN || |
|
357 |
+ errno == EINTR){ |
|
358 |
+ continue; |
|
359 |
+ }else{ |
|
360 |
+ LOG(L_CRIT,"BUG: tcp_receive_loop: read_fd: %s\n", |
|
336 | 361 |
strerror(errno)); |
337 |
- abort(); /* big error*/ |
|
362 |
+ abort(); /* big error*/ |
|
363 |
+ } |
|
338 | 364 |
} |
339 |
- } |
|
340 |
- if (n==0){ |
|
341 |
- fprintf(stderr, |
|
342 |
- "WARNING: tcp_receive_loop: 0 bytes read\n"); |
|
365 |
+ if (n==0){ |
|
366 |
+ LOG(L_ERR, "WARNING: tcp_receive_loop: 0 bytes read\n"); |
|
343 | 367 |
continue; |
344 |
- } |
|
345 |
- fprintf(stderr, "received n=%d id=%ld, fd=%d\n", n, id, s); |
|
346 |
- if (s==-1) { |
|
347 |
- fprintf(stderr, "ERROR: tcp_receive_loop: read_fd:" |
|
368 |
+ } |
|
369 |
+ DBG("received n=%d con=%ld, fd=%d\n", n, con, s); |
|
370 |
+ if (s==-1) { |
|
371 |
+ LOG(L_ERR, "ERROR: tcp_receive_loop: read_fd:" |
|
348 | 372 |
"no fd read\n"); |
349 | 373 |
state=-1; |
350 | 374 |
goto end_req; /* ?*/ |
375 |
+ } |
|
376 |
+ if (con==0){ |
|
377 |
+ LOG(L_ERR, "ERROR: tcp_receive_loop: null pointer\n"); |
|
378 |
+ state=-1; |
|
379 |
+ goto end_req; |
|
380 |
+ } |
|
381 |
+ con->fd=s; |
|
382 |
+ FD_SET(s, &master_set); |
|
383 |
+ if (maxfd<s) maxfd=s; |
|
384 |
+ tcpconn_listadd(list, con); |
|
351 | 385 |
} |
352 |
- |
|
353 |
- init_tcp_req(&req, s); |
|
354 |
- |
|
355 |
- |
|
356 |
- again: |
|
357 |
- while(req.complete==0 && req.error==TCP_REQ_OK){ |
|
358 |
- bytes=tcp_read_headers(&req); |
|
359 |
- /* if timeout state=0; goto end__req; */ |
|
386 |
+ for (con=list; con && nfds ; con=con->next){ |
|
387 |
+ if (FD_ISSET(con->fd, &sel_set)){ |
|
388 |
+ nfds--; |
|
389 |
+ req=&con->req; |
|
390 |
+again: |
|
391 |
+ while(req->complete==0 && req->error==TCP_REQ_OK){ |
|
392 |
+ bytes=tcp_read_headers(req, s); |
|
393 |
+ /* if timeout state=0; goto end__req; */ |
|
360 | 394 |
fprintf(stderr, "read= %d bytes, parsed=%d, state=%d, error=%d\n", |
361 |
- bytes, req.parsed-req.buf, req.state, req.error ); |
|
395 |
+ bytes, req->parsed-req->buf, req->state, req->error ); |
|
362 | 396 |
if (bytes==-1){ |
363 | 397 |
fprintf(stderr, "ERROR!\n"); |
364 | 398 |
state=-1; |
... | ... |
@@ -371,19 +405,19 @@ void tcp_receive_loop(int unix_sock) |
371 | 405 |
} |
372 | 406 |
|
373 | 407 |
} |
374 |
- if (req.error!=TCP_REQ_OK){ |
|
408 |
+ if (req->error!=TCP_REQ_OK){ |
|
375 | 409 |
fprintf(stderr, "bad request, state=%d, error=%d\n", |
376 |
- req.state, req.error); |
|
410 |
+ req->state, req->error); |
|
377 | 411 |
state=-1; |
378 | 412 |
goto end_req; |
379 | 413 |
} |
380 | 414 |
fprintf(stderr, "end of header part\n"); |
381 |
- fprintf(stderr, "headers:\n%.*s.\n",req.body-req.buf, req.buf); |
|
382 |
- if (req.has_content_len){ |
|
383 |
- fprintf(stderr, "content-length= %d\n", req.content_len); |
|
384 |
- fprintf(stderr, "body:\n%.*s\n", req.content_len, req.body); |
|
415 |
+ fprintf(stderr, "headers:\n%.*s.\n",req->body-req->buf, req->buf); |
|
416 |
+ if (req->has_content_len){ |
|
417 |
+ fprintf(stderr, "content-length= %d\n", req->content_len); |
|
418 |
+ fprintf(stderr, "body:\n%.*s\n", req->content_len, req->body); |
|
385 | 419 |
}else{ |
386 |
- req.error=TCP_REQ_BAD_LEN; |
|
420 |
+ req->error=TCP_REQ_BAD_LEN; |
|
387 | 421 |
fprintf(stderr, "content length not present or unparsable\n"); |
388 | 422 |
state=-1; |
389 | 423 |
goto end_req; |
... | ... |
@@ -393,21 +427,21 @@ void tcp_receive_loop(int unix_sock) |
393 | 427 |
state=0; |
394 | 428 |
/* just for debugging use sendipv4 as receiving socket */ |
395 | 429 |
DBG("calling receive_msg(%p, %d, %p)\n", |
396 |
- req.buf, (int)(req.parsed-req.buf), &sendipv4->su); |
|
430 |
+ req->buf, (int)(req->parsed-req->buf), &sendipv4->su); |
|
397 | 431 |
bind_address=sendipv4; |
398 |
- receive_msg(req.buf, req.parsed-req.buf, &sendipv4->su); |
|
432 |
+ receive_msg(req->buf, req->parsed-req->buf, &sendipv4->su); |
|
399 | 433 |
|
400 | 434 |
/* prepare for next request */ |
401 |
- size=req.pos-req.body; |
|
402 |
- if (size) memmove(req.buf, req.body, size); |
|
435 |
+ size=req->pos-req->body; |
|
436 |
+ if (size) memmove(req->buf, req->body, size); |
|
403 | 437 |
fprintf(stderr, "\npreparing for new request, kept %ld bytes\n", size); |
404 |
- req.pos=req.buf+size; |
|
405 |
- req.parsed=req.buf; |
|
406 |
- req.body=0; |
|
407 |
- req.error=TCP_REQ_OK; |
|
408 |
- req.state=H_STARTWS; |
|
409 |
- req.complete=req.content_len=req.has_content_len=0; |
|
410 |
- req.bytes_to_go=0; |
|
438 |
+ req->pos=req->buf+size; |
|
439 |
+ req->parsed=req->buf; |
|
440 |
+ req->body=0; |
|
441 |
+ req->error=TCP_REQ_OK; |
|
442 |
+ req->state=H_STARTWS; |
|
443 |
+ req->complete=req->content_len=req->has_content_len=0; |
|
444 |
+ req->bytes_to_go=0; |
|
411 | 445 |
|
412 | 446 |
/* process last req. */ |
413 | 447 |
|
... | ... |
@@ -418,7 +452,7 @@ void tcp_receive_loop(int unix_sock) |
418 | 452 |
/* release req & signal the parent */ |
419 | 453 |
if (s!=-1) close(s); |
420 | 454 |
/* errno==EINTR, EWOULDBLOCK a.s.o todo */ |
421 |
- response[0]=id; |
|
455 |
+ response[0]=con; |
|
422 | 456 |
response[1]=state; |
423 | 457 |
write(unix_sock, response, sizeof(response)); |
424 | 458 |
|