1 /* camserv - An internet streaming picture application
2 *
3 * Copyright (C) 1999-2002 Jon Travis (jtravis@p00p.org)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #include "camserv_config.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <signal.h>
25
26 #include "databuf.h"
27 #include "socket.h"
28 #include "sockset.h"
29 #include "manager.h"
30 #include "sock_field.h"
31 #include "log.h"
32
33 #define MODNAME "relay"
34
35 #define BACKLOG 20
36
37 #ifdef __DragonFly__
38 #include <errno.h>
39 #else
40 extern int errno;
41 #endif
42
43 #define CLIENT_T_UNK 0
44 #define CLIENT_T_CAMSERV 1
45 #define CLIENT_T_BROWSER 2
46 #define CLIENT_T_PROXY 3
47 #define CLIENT_T_SINGLE 4
48
49 #define CAMSTATE_SEND_PROXY 1 /* Sending proxy notifier */
50 #define CAMSTATE_RECV_SIZE 2 /* Receiving the size of the picture */
51 #define CAMSTATE_RECV_PICTURE 3 /* Receiving the actual picture */
52
53 #define BROWSERSTATE_SEND_PREAMBLE 0 /* Sending the preamble */
54 #define BROWSERSTATE_SEND_PICTURE 1 /* Sending the picture */
55 #define BROWSERSTATE_SEND_SEPERATOR 2 /* Sending the seperator */
56
57 #define PROXYCLSTATE_SEND_PICSIZE 0 /* Sending the pic size */
58 #define PROXYCLSTATE_SEND_PICTURE 1 /* Sending the actual picture */
59
60 typedef struct client_data_st {
61 int clienttype; /* one of CLIENT_T_* */
62 DataBuf *writebuf;
63 DataBuf *readbuf;
64
65 struct camdata_st { /* Valid only if clientttype == CAMSERV */
66 unsigned long int picsize; /* Picture size in network order */
67 char *picbuf; /* Actual picture data */
68 int camstate; /* State of camera */
69 } camdata;
70
71 struct browser_st {
72 int browserstate;
73 void *management_data;
74 int last_pic_id;
75 } browserdata;
76
77 struct proxycl_st {
78 int proxystate;
79 unsigned long int picsize; /* Picture size in network order */
80 void *management_data;
81 int last_pic_id;
82 char *pic_data;
83 } proxycldata;
84
85 char junkbuf[ 1024 ];
86 } ClientData;
87
88 typedef struct relay_data_st {
89 char camera_ip[ 1024 ]; /* IP of the remote camserv or relay */
90 int camera_port; /* Port of the remote camserv or relay */
91 Socket *camera_sock; /* Connection of camera sock */
92 } RelayData;
93
94 #define RANDOMSTRING "ThisRandomString"
95 #define CONTENTTYPE "image/jpeg"
96
97 static
get_preamble_text(size_t * len,int multi)98 char *get_preamble_text( size_t *len, int multi ){
99 #define MPREAMBLE_STR "HTTP/1.0 200 OK\n" \
100 "Content-type: multipart/x-mixed-replace;boundary=" RANDOMSTRING "\n" \
101 "Cache-Control: no-cache\n" \
102 "Cache-Control: private\n" \
103 "Pragma: no-cache\n\n" \
104 "--" RANDOMSTRING "\n" \
105 "Content-type: " CONTENTTYPE "\n\n"
106 #define SPREAMBLE_STR "HTTP/1.0 200 OK\n" \
107 "Content-type: " CONTENTTYPE "\n" \
108 "Cache-Control: no-cache\n" \
109 "Cache-Control: private\n" \
110 "Pragma: no-cache\n\n"
111
112 if( multi ) {
113 if( len != NULL ) *len = sizeof( MPREAMBLE_STR ) - 1;
114 return MPREAMBLE_STR;
115 } else {
116 if( len != NULL ) *len = sizeof( SPREAMBLE_STR ) - 1;
117 return SPREAMBLE_STR;
118 }
119 }
120
121 static
get_seperator_text(size_t * len)122 char *get_seperator_text( size_t *len ){
123 #define SEPERATOR_TEXT "\n--" RANDOMSTRING "\n" \
124 "Content-type: " CONTENTTYPE "\n\n" /* XXX */
125
126 if( len != NULL ) *len = sizeof( SEPERATOR_TEXT ) - 1;
127 return SEPERATOR_TEXT;
128 }
129
130
131 /*
132 * client_data_new: Create and initialize a new clientinfo structure.
133 *
134 * Return values: Returns NULL on failure, else a valid new clientdata
135 * on success.
136 */
137
138 static
client_data_new()139 ClientData *client_data_new(){
140 ClientData *res;
141
142 if( (res = malloc( sizeof( *res ))) == NULL )
143 return NULL;
144
145 if( (res->readbuf = databuf_new()) == NULL ){
146 free( res );
147 return NULL;
148 }
149
150 if( (res->writebuf = databuf_new()) == NULL ){
151 databuf_dest( res->readbuf );
152 free( res );
153 return NULL;
154 }
155
156 res->clienttype = CLIENT_T_UNK;
157 return res;
158 }
159
160 /*
161 * client_data_dest: Destroy a client data structure.
162 *
163 * Arguments: cldata = Clientdata to destroy
164 */
165
166 static
client_data_dest(ClientData * cldata)167 void client_data_dest( ClientData *cldata ){
168 databuf_dest( cldata->readbuf );
169 databuf_dest( cldata->writebuf );
170 free( cldata );
171 }
172
173 /*
174 * relay_connect_camserv: Connect to a camera server.
175 *
176 * Arguments: rdata = Relay data with info of where to connect.
177 *
178 * Return values: Returns -1 if the connection could not be made, else
179 * 0. If successful, rdata->camera_sock will be
180 * set to the new sock, else it will be set to NULL
181 */
182
183 static
relay_connect_camserv(SockField_Data * sfdata,RelayData * rdata)184 int relay_connect_camserv( SockField_Data *sfdata, RelayData *rdata ){
185 ClientData *cldata;
186
187 rdata->camera_sock = socket_connect( rdata->camera_ip, rdata->camera_port );
188 if( rdata->camera_sock == NULL )
189 return -1;
190
191 if( (cldata = client_data_new()) == NULL ){
192 camserv_log( MODNAME, "Error mallocing clientdata");
193 socket_dest( rdata->camera_sock );
194 return -1;
195 }
196
197 cldata->clienttype = CLIENT_T_CAMSERV;
198
199 if( sock_field_manage_socket( sfdata, rdata->camera_sock,
200 cldata ) == -1 )
201 {
202 camserv_log( MODNAME, "Error managing connect camera socket!");
203 client_data_dest( cldata );
204 socket_dest( rdata->camera_sock );
205 rdata->camera_sock = NULL;
206 return -1;
207 }
208
209 /* Send the proxy keyword */
210 databuf_buf_set( cldata->writebuf, "PROXY", sizeof( "PROXY" ));
211 cldata->camdata.picsize = 0;
212 cldata->camdata.picbuf = NULL;
213 cldata->camdata.camstate = CAMSTATE_SEND_PROXY;
214
215 return 0;
216 }
217
218 /*
219 * relay_init: Initialize the relay. This involves making the connection
220 * to the camera server if possible.
221 *
222 * Arguments: sfdata = Sockfield data passed from the almighty.
223 *
224 * Return values: Returns SOCKFIELD_OK on success, else SOCKFIELD_CLOSE.
225 */
226
227 static
relay_init(SockField_Data * sfdata,void * sys_cldata)228 int relay_init( SockField_Data *sfdata, void *sys_cldata ){
229 RelayData *rdata = sys_cldata;
230
231 if( relay_connect_camserv( sfdata, rdata ) == -1 )
232 camserv_log( MODNAME, "Couldn't connect to camserv: %s %d",
233 rdata->camera_ip, rdata->camera_port );
234
235 return SOCKFIELD_OK;
236 }
237
238 /*
239 * relay_preclose: Called by the field loop prior to closing a socket.
240 * This routine should cleanup any of the clientdata passed
241 * to the fieldloop management routines on socket init.
242 *
243 * Arguments: sock = Socket about to be closed.
244 * cldata = Clientdata passed in for socket @ manage time.
245 * sys_cldata = System clientdata, passed in as argument
246 * to main field_loop
247 */
248
249 static
relay_preclose(Socket * sock,void * cldata,void * sys_cldata)250 void relay_preclose( Socket *sock, void *cldata, void *sys_cldata ){
251 RelayData *rdata = sys_cldata;
252 ClientData *clientdata = cldata;
253
254 if( clientdata->clienttype == CLIENT_T_CAMSERV ) {
255 camserv_log( MODNAME, "Closing connection to camsera server!" );
256 /* Camera sock is now invalid */
257 rdata->camera_sock = NULL;
258 } else {
259 camserv_log( MODNAME, "Closing client from: \"%s\"",
260 socket_query_remote_name( sock ));
261 }
262 client_data_dest( cldata );
263 }
264
265 /*
266 * read_camera: This routine is called everytime the camserv socket has
267 * sent us some data to read. It is in 2 states the entire
268 * time -- that of getting the size of the next picture, then
269 * actually receiving the picture. After a successful
270 * receipt of a picture, it is then put into the management bins.
271 *
272 * Arguments: sfdata = SockField data
273 * sock = Socket of the camserv object
274 * cldata = Clientdata of the camserv object.
275 *
276 * Return values: Returns one of SOCKFIELD_*
277 */
278
279 static
read_camera(SockField_Data * sfdata,Socket * sock,ClientData * cldata)280 int read_camera( SockField_Data *sfdata, Socket *sock, ClientData *cldata ){
281 if( cldata->camdata.camstate == CAMSTATE_RECV_SIZE ) {
282 /* We just got the size ... malloc the data and attempt to receive it too*/
283 cldata->camdata.camstate = CAMSTATE_RECV_PICTURE;
284 cldata->camdata.picbuf = malloc( ntohl( cldata->camdata.picsize ));
285 if( cldata->camdata.picbuf == NULL ) {
286 camserv_log( MODNAME, "Couldn't malloc: %ld bytes for picture!",
287 (long)ntohl( cldata->camdata.picsize ));
288 return SOCKFIELD_CLOSE;
289 }
290 databuf_buf_set( cldata->readbuf, cldata->camdata.picbuf,
291 ntohl( cldata->camdata.picsize ) );
292 } else {
293 /* Else we just completed getting a whole picture */
294 cldata->camdata.camstate = CAMSTATE_RECV_SIZE;
295 databuf_buf_set( cldata->readbuf, &cldata->camdata.picsize,
296 sizeof( cldata->camdata.picsize ) );
297 if( manager_new_picture( cldata->camdata.picbuf,
298 ntohl( cldata->camdata.picsize ), 100 ) == -1 )
299 {
300 camserv_log( MODNAME, "Unable to manage picture!");
301 free( cldata->camdata.picbuf );
302 return SOCKFIELD_CLOSE;
303 }
304 sock_field_unhold_write( sfdata );
305 }
306 return SOCKFIELD_OK;
307 }
308
309 /*
310 * read_client: Called when a client has sent data to the relay. We just
311 * ignore everything the client sends us after initialization.
312 */
313
314 static
read_client(SockField_Data * sfdata,Socket * sock,ClientData * cldata)315 int read_client( SockField_Data *sfdata, Socket *sock, ClientData *cldata ){
316 /* Just refill the buffer .. client's that write anything, we are really
317 just disregarded */
318 databuf_buf_set( cldata->readbuf, &cldata->junkbuf[ 0 ],
319 sizeof( cldata->junkbuf ));
320 return SOCKFIELD_OK;
321 }
322
323 /*
324 * set_client_writebuf: Set the writebuffer of the client for the first
325 * time. This is usually the 'init' state for the
326 * client.
327 *
328 * Arguments: cldata = client to set the writebuffer for.
329 */
330
331 static
set_client_writebuf(ClientData * cldata)332 void set_client_writebuf( ClientData *cldata ){
333 size_t len;
334 char *cp;
335
336 if( cldata->clienttype == CLIENT_T_BROWSER ){
337 cp = get_preamble_text( &len, 1 );
338 cldata->browserdata.browserstate = BROWSERSTATE_SEND_PREAMBLE;
339 } else if( cldata->clienttype == CLIENT_T_SINGLE ) {
340 cp = get_preamble_text( &len, 0 );
341 cldata->browserdata.browserstate = BROWSERSTATE_SEND_PREAMBLE;
342 } else if( cldata->clienttype == CLIENT_T_PROXY ) {
343 cp = "";
344 len = 0;
345 cldata->proxycldata.proxystate = PROXYCLSTATE_SEND_PICTURE;
346 cldata->proxycldata.management_data = NULL;
347 } else {
348 camserv_log( MODNAME, "Unknown client state: \"%d\"", cldata->clienttype );
349 cp = "";
350 len = 0;
351 }
352
353 databuf_buf_set( cldata->writebuf, cp, len );
354 }
355
356 /*
357 * relay_client_read: Called by the sockfield loop when a socket has become
358 * readable -- On a client of which we have not identified
359 * prior, we determine their type by their initial msg.
360 * Otherwise on known sockets we attempt to read all the
361 * data we are currently trying to get from them, then
362 * dispatch them to the correct handling procedures
363 * (camserv, vs. client).
364 *
365 * Arguments: Standard sockfield callback arguments
366 *
367 * Return Values: Returns one of SOCKFIELD_*
368 */
369
370 static
relay_client_read(SockField_Data * sfdata,Socket * sock,void * cldata)371 int relay_client_read( SockField_Data *sfdata, Socket *sock, void *cldata ){
372 ClientData *clientdata = cldata;
373 int readres;
374 char sparebuf[ 1024 ];
375
376 if( clientdata->clienttype == CLIENT_T_UNK ){
377 read( socket_query_fd( sock ), sparebuf, sizeof( sparebuf ) );
378 sparebuf[ sizeof( sparebuf ) - 1 ] = '\0';
379 /* Client initializing itself */
380 if( !strncmp( sparebuf, "GET", 3 ) ){
381 if( strstr( sparebuf, "/singleframe" ))
382 clientdata->clienttype = CLIENT_T_SINGLE;
383 else
384 clientdata->clienttype = CLIENT_T_BROWSER;
385 } else if( !strncmp( sparebuf, "PROXY", 5 )) {
386 clientdata->clienttype = CLIENT_T_PROXY;
387 } else {
388 clientdata->clienttype = CLIENT_T_BROWSER;
389 }
390 databuf_buf_set( clientdata->readbuf, &clientdata->junkbuf[ 0 ],
391 sizeof( clientdata->junkbuf ));
392
393 set_client_writebuf( clientdata );
394 sock_field_unhold_write( sfdata );
395 return SOCKFIELD_OK;
396 }
397
398 readres = databuf_read( clientdata->readbuf, socket_query_fd( sock ));
399 if( readres == -1 ) return SOCKFIELD_CLOSE;
400 if( readres == 1 ) return SOCKFIELD_OK;
401
402 if( clientdata->clienttype == CLIENT_T_CAMSERV )
403 return read_camera( sfdata, sock, clientdata );
404 else
405 return read_client( sfdata, sock, clientdata );
406
407 return SOCKFIELD_OK;
408 }
409
410 /*
411 * write_camera: Called when our camserv socket is set to writeable. For
412 * most of the time we will just put ourselves on hold, however
413 * the initial connection with the camserv requires us to
414 * send the PROXY message.
415 *
416 * Return values: Returns one of SOCKFIELD_*
417 */
418
419 static
write_camera(SockField_Data * sfdata,Socket * sock,ClientData * cldata)420 int write_camera( SockField_Data *sfdata, Socket *sock, ClientData *cldata ){
421 if( cldata->camdata.camstate != CAMSTATE_SEND_PROXY ){
422 sock_field_hold_write( sfdata, sock );
423 return SOCKFIELD_OK;
424 }
425
426 /* Here we have just initialized our proxy with the connected camera. So
427 we put ourselves in a 'read' mode for the rest of the period */
428 cldata->camdata.camstate = CAMSTATE_RECV_SIZE;
429 databuf_buf_set( cldata->readbuf, &cldata->camdata.picsize,
430 sizeof( cldata->camdata.picsize ) );
431 return SOCKFIELD_OK;
432 }
433
434 /*
435 * write_client_proxy: Called when a client (who has previously identified
436 * itself as a proxy) has become writeable. Depending
437 * on the current state of the proxy, a picture will
438 * either be snagged from the management bins, or
439 * the picture will be sent.
440 *
441 * Return values: Returns one of SOCKFIELD_*
442 */
443
444 static
write_client_proxy(SockField_Data * sfdata,Socket * sock,ClientData * cldata)445 int write_client_proxy( SockField_Data *sfdata, Socket *sock,
446 ClientData *cldata )
447 {
448 if( cldata->proxycldata.proxystate == PROXYCLSTATE_SEND_PICTURE )
449 {
450 char *pic_data;
451 size_t pic_size;
452 int pic_id;
453
454 /* Just finished sending a picture */
455 if( cldata->proxycldata.management_data &&
456 manager_dest_client( cldata->proxycldata.management_data ) == -1 )
457 camserv_log( MODNAME, "Error destroying client proxy management!");
458
459 /* Get a new picture to send */
460 cldata->proxycldata.management_data =
461 manager_new_client( &pic_data, &pic_size, &pic_id );
462
463 if( cldata->proxycldata.management_data == NULL ){
464 camserv_log( MODNAME, "Error managing client proxy! "
465 "(no pictures may be available yet)" );
466 sock_field_hold_write( sfdata, sock );
467 return SOCKFIELD_OK;
468 }
469
470 if( pic_id == cldata->proxycldata.last_pic_id ) { /* Proxy read too fast */
471 manager_dest_client( cldata->proxycldata.management_data );
472 cldata->proxycldata.management_data = NULL;
473 sock_field_hold_write( sfdata, sock );
474 return SOCKFIELD_OK;
475 }
476
477 cldata->proxycldata.proxystate = PROXYCLSTATE_SEND_PICSIZE;
478 cldata->proxycldata.picsize = htonl( pic_size );
479 cldata->proxycldata.pic_data = pic_data;
480 cldata->proxycldata.last_pic_id = pic_id;
481 databuf_buf_set( cldata->writebuf,
482 &cldata->proxycldata.picsize,
483 sizeof( cldata->proxycldata.picsize ));
484 } else {
485 /* Else just finished sending the picture size */
486 cldata->proxycldata.proxystate = PROXYCLSTATE_SEND_PICTURE;
487 databuf_buf_set( cldata->writebuf,
488 cldata->proxycldata.pic_data,
489 ntohl( cldata->proxycldata.picsize ));
490 }
491
492 return SOCKFIELD_OK;
493 }
494
495 /*
496 * write_client_regular: Called when a regular client has been set writeable.
497 * in such a case we switch between all of the different
498 * states of a client and send the appropriate thing.
499 *
500 * Return values: Returns one of SOCKFIELD_*
501 */
502
503 static
write_client_regular(SockField_Data * sfdata,Socket * sock,ClientData * cldata)504 int write_client_regular( SockField_Data *sfdata, Socket *sock,
505 ClientData *cldata )
506 {
507 if( cldata->browserdata.browserstate == BROWSERSTATE_SEND_PREAMBLE ||
508 cldata->browserdata.browserstate == BROWSERSTATE_SEND_SEPERATOR )
509 {
510 char *pic_data;
511 size_t pic_size;
512 int pic_id;
513
514 cldata->browserdata.management_data =
515 manager_new_client( &pic_data, &pic_size, &pic_id );
516 if( cldata->browserdata.management_data == NULL ){
517 camserv_log( MODNAME, "Error managing client! "
518 "(no pictures may be available yet)");
519 sock_field_hold_write( sfdata, sock );
520 return SOCKFIELD_OK;
521 }
522
523 if( pic_id == cldata->browserdata.last_pic_id ) { /* Client reads fast */
524 manager_dest_client( cldata->browserdata.management_data );
525 cldata->browserdata.management_data = NULL;
526 sock_field_hold_write( sfdata, sock );
527 return SOCKFIELD_OK;
528 }
529
530 databuf_buf_set( cldata->writebuf, pic_data, pic_size );
531 cldata->browserdata.last_pic_id = pic_id;
532 cldata->browserdata.browserstate = BROWSERSTATE_SEND_PICTURE;
533 } else {
534 char *sep_data;
535 size_t sep_size;
536
537 if( manager_dest_client( cldata->browserdata.management_data ) == -1 ){
538 camserv_log( MODNAME, "Error destroying client management!");
539 }
540 cldata->browserdata.management_data = NULL;
541 cldata->browserdata.browserstate = BROWSERSTATE_SEND_SEPERATOR;
542 sep_data = get_seperator_text( &sep_size );
543 databuf_buf_set( cldata->writebuf, sep_data, sep_size );
544 }
545 return SOCKFIELD_OK;
546 }
547
548 static
write_client(SockField_Data * sfdata,Socket * sock,ClientData * cldata)549 int write_client( SockField_Data *sfdata, Socket *sock, ClientData *cldata ){
550 /* If client hasn't initialized itself with us yet, put him on hold */
551 if( cldata->clienttype == CLIENT_T_UNK ){
552 sock_field_hold_write( sfdata, sock );
553 return SOCKFIELD_OK;
554 }
555
556 if( cldata->clienttype == CLIENT_T_PROXY )
557 return write_client_proxy( sfdata, sock, cldata );
558 else
559 return write_client_regular( sfdata, sock, cldata );
560
561 return SOCKFIELD_OK;
562 }
563
564 static
relay_client_write(SockField_Data * sfdata,Socket * sock,void * cldata)565 int relay_client_write( SockField_Data *sfdata, Socket *sock, void *cldata ){
566 ClientData *clientdata = cldata;
567 int writeres;
568
569 writeres = databuf_write( clientdata->writebuf, socket_query_fd( sock ));
570 if( writeres == -1 ) return SOCKFIELD_CLOSE;
571 if( writeres == 1 ) return SOCKFIELD_OK; /* Still data left to write */
572 /* Else we have written all the data we were supposed to */
573
574 if( clientdata->clienttype == CLIENT_T_CAMSERV )
575 return write_camera( sfdata, sock, clientdata );
576 else
577 return write_client( sfdata, sock, clientdata );
578
579 return SOCKFIELD_OK;
580 }
581
582 static
relay_accept(SockField_Data * sfdata,Socket * listen_sock,void * sys_cldata)583 void relay_accept( SockField_Data *sfdata, Socket *listen_sock,
584 void *sys_cldata )
585 {
586 ClientData *cldata;
587 Socket *new_socket;
588
589 if( (new_socket = socket_accept( listen_sock )) == NULL ){
590 camserv_log( MODNAME, "Error accepting new socket!");
591 return;
592 }
593
594 if( (cldata = client_data_new()) == NULL ){
595 camserv_log( MODNAME, "Error allocating clientdata!");
596 socket_dest( new_socket );
597 return;
598 }
599
600 cldata->clienttype = CLIENT_T_UNK;
601 if( sock_field_manage_socket( sfdata, new_socket, cldata ) == -1 ){
602 camserv_log( MODNAME, "Error managing new socket" );
603 socket_dest( new_socket );
604 client_data_dest( cldata );
605 return;
606 }
607 camserv_log( MODNAME, "Accepted client: \"%s\"",
608 socket_query_remote_name( new_socket ));
609 }
610
611
612 static
relay_timeout(SockField_Data * sfdata,void * sys_cldata)613 void relay_timeout( SockField_Data *sfdata, void *sys_cldata ){
614 RelayData *rdata = sys_cldata;
615
616 if( rdata->camera_sock == NULL ) {
617 camserv_log( MODNAME, "Attempting connect to camserver");
618 if( relay_connect_camserv( sfdata, rdata ) == -1 ){
619 camserv_log( MODNAME, "Error connecting to camserv: %s %d",
620 rdata->camera_ip, rdata->camera_port );
621 }
622 }
623 }
624
625
main(int argc,char * argv[])626 int main( int argc, char *argv[] ){
627 Socket *listen_sock;
628 int localport, remoteport;
629 RelayData rdata;
630 struct timeval retry_time;
631
632 if( argc < 3 ) {
633 fprintf( stderr, "camserv relay v%s - by Jon Travis (jtravis@p00p.org)\n",
634 VERSION );
635 fprintf( stderr, "Syntax: %s <port> <camserv IP> <ccamserv port>\n",
636 argv[ 0 ]);
637 fprintf( stderr, "\tThe relay will bind port <port> on the localhost\n");
638 fprintf( stderr, "\tand connect to the camserv port to do the relay.\n");
639 return -1;
640 }
641
642 signal( SIGPIPE, SIG_IGN );
643 if( sscanf( argv[1], "%d", &localport ) != 1 ) {
644 camserv_log( MODNAME, "Error: port \"%s\" invalid!", argv[ 1 ] );
645 return -1;
646 }
647
648 if( localport < 1024 )
649 camserv_log( MODNAME, "Warning: Port numbers < 1024 shouldn't be used");
650
651 if( sscanf( argv[3], "%d", &remoteport ) != 1 ) {
652 camserv_log( MODNAME, "Error: camserv port \"%s\" invalid!", argv[3] );
653 return -1;
654 }
655
656 strncpy( rdata.camera_ip, argv[2], sizeof( rdata.camera_ip ) - 1);
657 rdata.camera_ip[ sizeof( rdata.camera_ip ) - 1 ] = '\0';
658 rdata.camera_port = remoteport;
659 rdata.camera_sock = NULL;
660 if( (listen_sock = socket_serve_tcp( NULL, localport, BACKLOG )) == NULL ){
661 camserv_log( MODNAME, "Could not bind local port: %s",
662 strerror( errno ));
663 return -1;
664 }
665
666 retry_time.tv_sec = 10;
667 retry_time.tv_usec = 0;
668 if( sock_field( listen_sock, &rdata, relay_init, relay_accept,
669 relay_client_read, relay_client_write,
670 relay_preclose, relay_timeout, &retry_time ) == -1 )
671 {
672 socket_dest( listen_sock );
673 camserv_log( MODNAME, "Camserv relay abnormally terminated");
674 return -1;
675 }
676 socket_dest( listen_sock );
677 return 0;
678 }
679