source: ntrip/trunk/ntripserver/ntripserver.c@ 9450

Last change on this file since 9450 was 9450, checked in by stuerze, 3 years ago

minor changes

  • Property svn:keywords set to Id Revision Date
File size: 77.0 KB
RevLine 
[485]1/*
[8784]2 * $Id: ntripserver.c 9450 2021-06-04 07:12:25Z stuerze $
[485]3 *
[8784]4 * Copyright (c) 2003...2019
[485]5 * German Federal Agency for Cartography and Geodesy (BKG)
[8784]6 * Dirk Stöcker (Alberding GmbH)
[485]7 *
8 * Developed for Networked Transport of RTCM via Internet Protocol (NTRIP)
9 * for streaming GNSS data over the Internet.
10 *
11 * Designed by Informatik Centrum Dortmund http://www.icd.de
12 *
13 * The BKG disclaims any liability nor responsibility to any person or
14 * entity with respect to any loss or damage caused, or alleged to be
15 * caused, directly or indirectly by the use and application of the NTRIP
16 * technology.
17 *
18 * For latest information and updates, access:
[8784]19 * https://igs.bkg.bund.de/ntrip/index
[485]20 *
[8784]21 * BKG, Frankfurt, Germany, August 2019
[485]22 * E-mail: euref-ip@bkg.bund.de
23 *
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * as published by the Free Software Foundation; either version 2
27 * of the License, or (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
[8784]34 * You should have received a copy of the GNU General Public License along
35 * with this program; if not, write to the Free Software Foundation, Inc.,
36 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
[485]37 */
38
[8784]39/* SVN revision and version */
40static char revisionstr[] = "$Revision: 9450 $";
[9401]41static char datestr[] = "$Date: 2021-06-04 07:12:25 +0000 (Fri, 04 Jun 2021) $";
[485]42
43#include <ctype.h>
44#include <errno.h>
45#include <fcntl.h>
46#include <getopt.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
[653]50#include <sys/time.h>
51#include <sys/types.h>
52#include <time.h>
53#include <signal.h>
[485]54#include <unistd.h>
55
[653]56#ifdef WINDOWSVERSION
57 #include <winsock2.h>
58 #include <io.h>
[1570]59 #include <sys/stat.h>
60 #include <windows.h>
[653]61 typedef SOCKET sockettype;
62 typedef u_long in_addr_t;
63 typedef size_t socklen_t;
64 typedef u_short uint16_t;
65#else
[9401]66typedef int sockettype;
67#include <arpa/inet.h>
68#include <sys/socket.h>
69#include <netinet/in.h>
70#include <netdb.h>
71#include <sys/termios.h>
72#define closesocket(sock) close(sock)
73#define INVALID_HANDLE_VALUE -1
74#define INVALID_SOCKET -1
[653]75#endif
76
[485]77#ifndef COMPILEDATE
78#define COMPILEDATE " built " __DATE__
79#endif
80
[653]81#define ALARMTIME (2*60)
82
[485]83#ifndef MSG_DONTWAIT
84#define MSG_DONTWAIT 0 /* prevent compiler errors */
85#endif
86#ifndef O_EXLOCK
87#define O_EXLOCK 0 /* prevent compiler errors */
88#endif
89
[9401]90enum MODE {
91 SERIAL = 1,
92 TCPSOCKET = 2,
93 INFILE = 3,
94 SISNET = 4,
95 UDPSOCKET = 5,
96 NTRIP1_IN = 6,
97 NTRIP2_HTTP_IN = 7, // HTTP only
98 LAST
99};
[485]100
[9401]101enum OUTMODE {
102 HTTP = 1,
103 RTSP = 2,
104 NTRIP1 = 3,
105 UDP = 4,
[9448]106 TCPIP = 5,
[9401]107 END
108};
[485]109
[493]110#define AGENTSTRING "NTRIP NtripServerPOSIX"
[8789]111#define BUFSZ 10240
[485]112#define SZ 64
113
114/* default socket source */
115#define SERV_HOST_ADDR "localhost"
116#define SERV_TCP_PORT 2101
117
118/* default destination */
[9404]119#define NTRIP_CASTER "euref-ip.net"
[485]120#define NTRIP_PORT 2101
121
122#define SISNET_SERVER "131.176.49.142"
123#define SISNET_PORT 7777
124
125#define RTP_VERSION 2
126#define TIME_RESOLUTION 125
127
[9401]128static int ttybaud = 19200;
[2273]129#ifndef WINDOWSVERSION
[9401]130static const char *ttyport = "/dev/gps";
[2273]131#else
[9401]132 static const char *ttyport = "COM1";
[2273]133#endif
[9401]134static const char *filepath = "/dev/stdin";
135static enum MODE inputmode = INFILE;
136static int sisnet = 31;
137static int gps_file = -1;
138static sockettype gps_socket = INVALID_SOCKET;
139static sockettype socket_tcp = INVALID_SOCKET;
140static sockettype socket_udp = INVALID_SOCKET;
[653]141#ifndef WINDOWSVERSION
[9401]142static int gps_serial = INVALID_HANDLE_VALUE;
143static int sigpipe_received = 0;
[653]144#else
[9401]145 HANDLE gps_serial = INVALID_HANDLE_VALUE;
[653]146#endif
[9401]147static int sigalarm_received = 0;
148static int sigint_received = 0;
149static int reconnect_sec = 1;
150static const char *casterouthost = NTRIP_CASTER;
[915]151static char rtsp_extension[SZ] = "";
[9401]152static const char *mountpoint = NULL;
153static int udp_cseq = 1;
[1570]154static int udp_tim, udp_seq, udp_init;
[485]155
156/* Forward references */
[653]157static void send_receive_loop(sockettype sock, int outmode,
[9401]158 struct sockaddr *pcasterRTP, socklen_t length, unsigned int rtpssrc,
159 int chnunkymode);
160static void usage(int, char*);
161static int encode(char *buf, int size, const char *user, const char *pwd);
162static int send_to_caster(char *input, sockettype socket, int input_size);
[915]163static void close_session(const char *caster_addr, const char *mountpoint,
[9401]164 int session, char *rtsp_ext, int fallback);
165static int reconnect(int rec_sec, int rec_sec_max);
[485]166static void handle_sigint(int sig);
[653]167static void setup_signal_handler(int sig, void (*handler)(int));
168#ifndef WINDOWSVERSION
[9401]169static int openserial(const char *tty, int blocksz, int baud);
[653]170static void handle_sigpipe(int sig);
[487]171static void handle_alarm(int sig);
[653]172#else
[9401]173 static HANDLE openserial(const char * tty, int baud);
[1570]174#endif
[485]175
[9401]176/*
177 * main
178 *
179 * Main entry point for the program. Processes command-line arguments and
180 * prepares for action.
181 *
182 * Parameters:
183 * argc : integer : Number of command-line arguments.
184 * argv : array of char : Command-line arguments as an array of
185 * zero-terminated pointers to strings.
186 *
187 * Return Value:
188 * The function does not return a value (although its return type is int).
189 *
190 * Remarks:
191 *
192 */
193int main(int argc, char **argv) {
194 int c;
195 int size = 2048; /* for setting send buffer size */
196 struct sockaddr_in caster;
197 const char *proxyhost = "";
198 unsigned int proxyport = 0;
[653]199
[485]200 /*** INPUT ***/
[9401]201 const char *casterinhost = 0;
202 unsigned int casterinport = 0;
203 const char *inhost = 0;
204 unsigned int inport = 0;
205 int chunkymode = 0;
206 char get_extension[SZ] = "";
[485]207
[9401]208 struct hostent *he;
[485]209
[9401]210 const char *sisnetpassword = "";
211 const char *sisnetuser = "";
[485]212
[9401]213 const char *stream_name = 0;
214 const char *stream_user = 0;
215 const char *stream_password = 0;
[485]216
[9401]217 const char *recvrid = 0;
218 const char *recvrpwd = 0;
[485]219
[9401]220 const char *initfile = NULL;
[485]221
[9401]222 int bindmode = 0;
[485]223
224 /*** OUTPUT ***/
[9401]225 unsigned int casteroutport = NTRIP_PORT;
226 const char *outhost = 0;
227 unsigned int outport = 0;
228 char post_extension[SZ] = "";
[485]229
[9401]230 const char *ntrip_str = "";
[485]231
[9401]232 const char *user = "";
233 const char *password = "";
[485]234
[9401]235 int outputmode = NTRIP1;
[485]236
237 struct sockaddr_in casterRTP;
238 struct sockaddr_in local;
[9401]239 int client_port = 0;
240 int server_port = 0;
241 unsigned int session = 0;
242 socklen_t len = 0;
243 int i = 0;
[485]244
[9401]245 char szSendBuffer[BUFSZ];
246 char authorization[SZ];
247 int nBufferBytes = 0;
248 char *dlim = " \r\n=";
249 char *token;
250 char *tok_buf[BUFSZ];
[485]251
[9401]252 int reconnect_sec_max = 0;
[487]253
[485]254 setbuf(stdout, 0);
255 setbuf(stdin, 0);
256 setbuf(stderr, 0);
257
258 {
[8784]259 char *a;
260 int i = 2;
261 strcpy(revisionstr, "1.");
[9401]262 for (a = revisionstr + 11; *a && *a != ' '; ++a)
[8784]263 revisionstr[i++] = *a;
264 revisionstr[i] = 0;
265 i = 0;
[9401]266 for (a = datestr + 7; *a && *a != ' '; ++a)
[8784]267 datestr[i++] = *a;
268 datestr[i] = 0;
[485]269 }
270
[653]271 /* setup signal handler for CTRL+C */
272 setup_signal_handler(SIGINT, handle_sigint);
[1570]273#ifndef WINDOWSVERSION
[653]274 /* setup signal handler for boken pipe */
275 setup_signal_handler(SIGPIPE, handle_sigpipe);
[485]276 /* setup signal handler for timeout */
[487]277 setup_signal_handler(SIGALRM, handle_alarm);
[485]278 alarm(ALARMTIME);
[653]279#else
280 /* winsock initialization */
281 WSADATA wsaData;
282 if (WSAStartup(MAKEWORD(1,1), &wsaData))
283 {
284 fprintf(stderr, "Could not init network access.\n");
285 return 20;
286 }
287#endif
[1570]288
[485]289 /* get and check program arguments */
[9401]290 if (argc <= 1) {
[485]291 usage(2, argv[0]);
292 exit(1);
293 }
[9401]294 while ((c = getopt(argc, argv,
[9448]295 "M:i:h:b:p:s:a:m:c:H:P:f:x:y:l:u:V:D:U:W:O:E:F:R:N:n:B")) != EOF) {
[9401]296 switch (c) {
297 case 'M': /*** InputMode ***/
298 if (!strcmp(optarg, "serial"))
299 inputmode = SERIAL;
300 else if (!strcmp(optarg, "tcpsocket"))
301 inputmode = TCPSOCKET;
302 else if (!strcmp(optarg, "file"))
303 inputmode = INFILE;
304 else if (!strcmp(optarg, "sisnet"))
305 inputmode = SISNET;
306 else if (!strcmp(optarg, "udpsocket"))
307 inputmode = UDPSOCKET;
308 else if (!strcmp(optarg, "ntrip1"))
309 inputmode = NTRIP1_IN;
310 else if (!strcmp(optarg, "ntrip2http"))
311 inputmode = NTRIP2_HTTP_IN;
312 else
313 inputmode = atoi(optarg);
314 if ((inputmode == 0) || (inputmode >= LAST)) {
315 fprintf(stderr, "ERROR: can't convert <%s> to a valid InputMode\n",
316 optarg);
317 usage(-1, argv[0]);
318 }
319 break;
320 case 'i': /* serial input device */
321 ttyport = optarg;
322 break;
323 case 'B': /* bind to incoming UDP stream */
324 bindmode = 1;
325 break;
326 case 'V': /* Sisnet data server version number */
327 if (!strcmp("3.0", optarg))
328 sisnet = 30;
329 else if (!strcmp("3.1", optarg))
330 sisnet = 31;
331 else if (!strcmp("2.1", optarg))
332 sisnet = 21;
333 else {
334 fprintf(stderr, "ERROR: unknown SISNeT version <%s>\n", optarg);
335 usage(-2, argv[0]);
336 }
337 break;
338 case 'b': /* serial input baud rate */
339 ttybaud = atoi(optarg);
340 if (ttybaud <= 1) {
341 fprintf(stderr,
342 "ERROR: can't convert <%s> to valid serial baud rate\n", optarg);
343 usage(1, argv[0]);
344 }
345 break;
346 case 'a': /* Destination caster address */
347 casterouthost = optarg;
348 break;
349 case 'p': /* Destination caster port */
350 casteroutport = atoi(optarg);
351 if (casteroutport <= 1 || casteroutport > 65535) {
352 fprintf(stderr,
353 "ERROR: can't convert <%s> to a valid HTTP server port\n",
354 optarg);
355 usage(1, argv[0]);
356 }
357 break;
358 case 'm': /* Destination caster mountpoint for stream upload */
359 mountpoint = optarg;
360 break;
361 case 's': /* File name for input data simulation from file */
362 filepath = optarg;
363 break;
364 case 'f': /* name of an initialization file */
365 initfile = optarg;
366 break;
367 case 'x': /* user ID to access incoming stream */
368 recvrid = optarg;
369 break;
370 case 'y': /* password to access incoming stream */
371 recvrpwd = optarg;
372 break;
373 case 'u': /* Sisnet data server user ID */
374 sisnetuser = optarg;
375 break;
376 case 'l': /* Sisnet data server password */
377 sisnetpassword = optarg;
378 break;
379 case 'c': /* DestinationCaster password for stream upload to mountpoint */
380 password = optarg;
381 break;
382 case 'H': /* Input host address*/
383 casterinhost = optarg;
384 break;
385 case 'P': /* Input port */
386 casterinport = atoi(optarg);
387 if (casterinport <= 1 || casterinport > 65535) {
388 fprintf(stderr, "ERROR: can't convert <%s> to a valid port number\n",
389 optarg);
390 usage(1, argv[0]);
391 }
392 break;
393 case 'D': /* Source caster mountpoint for stream input */
394 stream_name = optarg;
395 break;
396 case 'U': /* Source caster user ID for input stream access */
397 stream_user = optarg;
398 break;
399 case 'W': /* Source caster password for input stream access */
400 stream_password = optarg;
401 break;
402 case 'E': /* Proxy Server */
403 proxyhost = optarg;
404 break;
405 case 'F': /* Proxy port */
406 proxyport = atoi(optarg);
407 break;
408 case 'R': /* maximum delay between reconnect attempts in seconds */
409 reconnect_sec_max = atoi(optarg);
410 break;
411 case 'O': /* OutputMode */
412 outputmode = 0;
413 if (!strcmp(optarg, "n") || !strcmp(optarg, "ntrip1"))
414 outputmode = NTRIP1;
415 else if (!strcmp(optarg, "h") || !strcmp(optarg, "http"))
416 outputmode = HTTP;
417 else if (!strcmp(optarg, "r") || !strcmp(optarg, "rtsp"))
418 outputmode = RTSP;
419 else if (!strcmp(optarg, "u") || !strcmp(optarg, "udp"))
420 outputmode = UDP;
[9448]421 else if (!strcmp(optarg, "t") || !strcmp(optarg, "tcpip"))
422 outputmode = TCPIP;
[9401]423 else
424 outputmode = atoi(optarg);
425 if ((outputmode == 0) || (outputmode >= END)) {
426 fprintf(stderr, "ERROR: can't convert <%s> to a valid OutputMode\n",
427 optarg);
428 usage(-1, argv[0]);
429 }
430 break;
431 case 'n': /* Destination caster user ID for stream upload to mountpoint */
432 user = optarg;
433 break;
434 case 'N': /* Ntrip-STR, optional for Ntrip Version 2.0 */
435 ntrip_str = optarg;
436 break;
437 case 'h': /* print help screen */
438 case '?':
439 usage(0, argv[0]);
440 break;
441 default:
442 usage(2, argv[0]);
443 break;
[485]444 }
445 }
446
447 argc -= optind;
448 argv += optind;
449
450 /*** argument analysis ***/
[9401]451 if (argc > 0) {
[485]452 fprintf(stderr, "ERROR: Extra args on command line: ");
[9401]453 for (; argc > 0; argc--) {
[485]454 fprintf(stderr, " %s", *argv++);
455 }
456 fprintf(stderr, "\n");
[9401]457 usage(1, argv[0]); /* never returns */
[485]458 }
459
[9401]460 if ((reconnect_sec_max > 0) && (reconnect_sec_max < 256)) {
[487]461 fprintf(stderr,
[9401]462 "WARNING: maximum delay between reconnect attempts changed from %d to 256 seconds\n",
463 reconnect_sec_max);
[487]464 reconnect_sec_max = 256;
465 }
466
[9448]467 if (!mountpoint && outputmode != TCPIP) {
[485]468 fprintf(stderr, "ERROR: Missing mountpoint argument for stream upload\n");
469 exit(1);
470 }
[9448]471 if (outputmode == TCPIP) {
[9401]472 mountpoint = NULL;
[485]473 }
[9401]474
475 if (!password[0]) {
[9448]476 if (outputmode != TCPIP)
[9401]477 fprintf(stderr,
478 "WARNING: Missing password argument for stream upload - are you really sure?\n");
479 } else {
[485]480 nBufferBytes += encode(authorization, sizeof(authorization), user,
[9401]481 password);
482 if (nBufferBytes > (int) sizeof(authorization)) {
[485]483 fprintf(stderr, "ERROR: user ID and/or password too long: %d (%d)\n"
[9401]484 " user ID: %s \npassword: <%s>\n", nBufferBytes,
485 (int) sizeof(authorization), user, password);
[485]486 exit(1);
487 }
488 }
489
[9401]490 if (stream_name && stream_user && !stream_password) {
[9403]491 fprintf(stderr, "WARNING: Missing password argument for stream download - are you really sure?\n");
[485]492 }
493
494 /*** proxy server handling ***/
[9401]495 if (*proxyhost) {
[9448]496 inhost = proxyhost;
497 inport = proxyport;
[9401]498 i = snprintf(szSendBuffer, sizeof(szSendBuffer), "http://%s:%d", casterinhost, casterinport);
499 if ((i > SZ) || (i < 0)) {
500 fprintf(stderr,
501 "ERROR: Destination caster name/port to long - length = %d (max: %d)\n",
502 i, SZ);
[485]503 exit(0);
[9401]504 } else {
505 strncpy(get_extension, szSendBuffer, (size_t) i);
[485]506 strcpy(szSendBuffer, "");
507 i = 0;
508 }
[9448]509 if (strstr(casterouthost, "127.0.0.1") ||
510 strstr(casterouthost, "localhost")) {
511 outhost = casterouthost;
512 outport = casteroutport;
513 }
514 else {
515 outhost = proxyhost;
516 outport = proxyport;
517 i = snprintf(szSendBuffer, sizeof(szSendBuffer), "http://%s:%d", casterouthost, casteroutport);
518 if ((i > SZ) || (i < 0)) {
519 fprintf(stderr,
520 "ERROR: Destination caster name/port to long - length = %d (max: %d)\n",
521 i, SZ);
522 exit(0);
523 } else {
524 strncpy(post_extension, szSendBuffer, (size_t) i);
525 strcpy(szSendBuffer, "");
526 i = snprintf(szSendBuffer, sizeof(szSendBuffer), ":%d", casteroutport);
527 strncpy(rtsp_extension, szSendBuffer, SZ);
528 strcpy(szSendBuffer, ""); i = 0;
529 }
530 }
531
[9401]532 } else {
533 outhost = casterouthost;
534 outport = casteroutport;
535 inhost = casterinhost;
536 inport = casterinport;
[485]537 }
538
[9401]539 while (inputmode != LAST) {
[487]540 int input_init = 1;
[9401]541 if (sigint_received)
542 break;
[487]543 /*** InputMode handling ***/
[9401]544 switch (inputmode) {
545 case INFILE: {
546 if ((gps_file = open(filepath, O_RDONLY)) < 0) {
[487]547 perror("ERROR: opening input file");
548 exit(1);
[1570]549 }
[653]550#ifndef WINDOWSVERSION
[1570]551 /* set blocking inputmode in case it was not set
[9401]552 (seems to be sometimes for fifo's) */
[1570]553 fcntl(gps_file, F_SETFL, 0);
[653]554#endif
[1570]555 printf("file input: file = %s\n", filepath);
[485]556 }
[9401]557 break;
558 case SERIAL: /* open serial port */ {
[653]559#ifndef WINDOWSVERSION
[1570]560 gps_serial = openserial(ttyport, 1, ttybaud);
[653]561#else
[1570]562 gps_serial = openserial(ttyport, ttybaud);
[653]563#endif
[9401]564 if (gps_serial == INVALID_HANDLE_VALUE)
565 exit(1);
[1570]566 printf("serial input: device = %s, speed = %d\n", ttyport, ttybaud);
[1113]567
[9401]568 if (initfile) {
[1113]569 char buffer[1024];
570 FILE *fh;
571 int i;
572
[9401]573 if ((fh = fopen(initfile, "r"))) {
574 while ((i = fread(buffer, 1, sizeof(buffer), fh)) > 0) {
[1113]575#ifndef WINDOWSVERSION
[9401]576 if ((write(gps_serial, buffer, i)) != i) {
[1570]577 perror("WARNING: sending init file");
578 input_init = 0;
579 break;
[1113]580 }
581#else
[1570]582 DWORD nWrite = -1;
[9401]583 if(!WriteFile(gps_serial, buffer, sizeof(buffer), &nWrite, NULL)) {
[1113]584 fprintf(stderr,"ERROR: sending init file \n");
[1570]585 input_init = 0;
[1113]586 break;
587 }
588 i = (int)nWrite;
589#endif
590 }
[9401]591 if (i < 0) {
[1113]592 perror("ERROR: reading init file");
593 reconnect_sec_max = 0;
594 input_init = 0;
595 break;
596 }
597 fclose(fh);
[9401]598 } else {
[1113]599 fprintf(stderr, "ERROR: can't read init file <%s>\n", initfile);
600 reconnect_sec_max = 0;
601 input_init = 0;
602 break;
603 }
[1570]604 }
[485]605 }
[9401]606 break;
607 case TCPSOCKET:
608 case UDPSOCKET:
609 case SISNET:
610 case NTRIP1_IN:
611 case NTRIP2_HTTP_IN: {
612 if (inputmode == SISNET) {
613 if (!inhost)
614 inhost = SISNET_SERVER;
615 if (!inport)
616 inport = SISNET_PORT;
617 } else if (inputmode == NTRIP1_IN || inputmode == NTRIP2_HTTP_IN) {
618 if (!inport)
619 inport = NTRIP_PORT;
620 if (!inhost)
621 inhost = NTRIP_CASTER;
622 } else if ((inputmode == TCPSOCKET) || (inputmode == UDPSOCKET)) {
623 if (!inport)
624 inport = SERV_TCP_PORT;
625 if (!inhost)
626 inhost = SERV_HOST_ADDR;
[1570]627 }
[485]628
[9401]629 if (!(he = gethostbyname(inhost))) {
[487]630 fprintf(stderr, "ERROR: Input host <%s> unknown\n", inhost);
631 usage(-2, argv[0]);
[1570]632 }
[485]633
[9401]634 if ((gps_socket = socket(AF_INET, inputmode == UDPSOCKET ? SOCK_DGRAM : SOCK_STREAM, 0)) == INVALID_SOCKET) {
[487]635 fprintf(stderr,
[9401]636 "ERROR: can't create socket for incoming data stream\n");
[487]637 exit(1);
[1570]638 }
[485]639
[9401]640 memset((char*) &caster, 0x00, sizeof(caster));
641 if (!bindmode)
[487]642 memcpy(&caster.sin_addr, he->h_addr, (size_t)he->h_length);
[1570]643 caster.sin_family = AF_INET;
644 caster.sin_port = htons(inport);
[485]645
[1570]646 fprintf(stderr, "%s input: host = %s, port = %d, %s%s%s%s%s\n",
[9401]647 inputmode == NTRIP1_IN ? "ntrip1" :
648 inputmode == NTRIP2_HTTP_IN ? "ntrip2" :
649 inputmode == SISNET ? "sisnet" :
650 inputmode == TCPSOCKET ? "tcp socket" : "udp socket",
651 bindmode ? "127.0.0.1" : inet_ntoa(caster.sin_addr), inport,
652 stream_name ? "stream = " : "", stream_name ? stream_name : "",
653 initfile ? ", initfile = " : "", initfile ? initfile : "",
654 bindmode ? "binding mode" : "");
[485]655
[9401]656 if (bindmode) {
657 if (bind(gps_socket, (struct sockaddr*) &caster, sizeof(caster))
658 < 0) {
[487]659 fprintf(stderr, "ERROR: can't bind input to port %d\n", inport);
660 reconnect_sec_max = 0;
661 input_init = 0;
662 break;
663 }
[1570]664 } /* connect to input-caster or proxy server*/
[9448]665 else if (connect(gps_socket, (struct sockaddr*) &caster, sizeof(caster)) < 0) {
[487]666 fprintf(stderr, "WARNING: can't connect input to %s at port %d\n",
[9401]667 inet_ntoa(caster.sin_addr), inport);
[487]668 input_init = 0;
669 break;
[1570]670 }
[485]671
[9401]672 /* input from NTRIP caster */
673 int init = 0;
674 /* set socket buffer size */
675 setsockopt(gps_socket, SOL_SOCKET, SO_SNDBUF, (const char*) &size, sizeof(const char*));
676 /* input from Ntrip caster*/
677 nBufferBytes=snprintf(szSendBuffer, sizeof(szSendBuffer) - 40,/* leave some space for login */
678 "GET %s/%s HTTP/1.1\r\n"
679 "Host: %s\r\n"
680 "%s"
681 "User-Agent: %s/%s\r\n"
682 //"%s%s%s" // nmea
683 "Connection: close%s",
684 get_extension,
685 stream_name ? stream_name : "",
686 casterinhost,
687 inputmode == NTRIP1_IN ? "" : "Ntrip-Version: Ntrip/2.0\r\n",
688 AGENTSTRING, revisionstr,
689 //args.nmea ? "Ntrip-GGA: " : "", args.nmea ? args.nmea : "", args.nmea ? "\r\n" : "", // TODO: add argument
690 (*stream_user || *stream_password) ? "\r\nAuthorization: Basic " : "");
691 /* second check for old glibc */
692 if (nBufferBytes > (int) sizeof(szSendBuffer) - 40 || nBufferBytes < 0) {
693 fprintf(stderr, "ERROR: Source caster request too long\n");
694 input_init = 0;
695 reconnect_sec_max = 0;
696 break;
697 }
698 nBufferBytes += encode(szSendBuffer + nBufferBytes, sizeof(szSendBuffer) - nBufferBytes - 4,
699 stream_user, stream_password);
700 if (nBufferBytes > (int) sizeof(szSendBuffer) - 4) {
701 fprintf(stderr, "ERROR: Source caster user ID and/or password too long\n");
702 input_init = 0;
703 reconnect_sec_max = 0;
704 break;
705 }
706 szSendBuffer[nBufferBytes++] = '\r';
707 szSendBuffer[nBufferBytes++] = '\n';
708 szSendBuffer[nBufferBytes++] = '\r';
709 szSendBuffer[nBufferBytes++] = '\n';
[9448]710 fprintf(stdout, "%s\n", szSendBuffer);
[9401]711 if ((send(gps_socket, szSendBuffer, (size_t) nBufferBytes, 0)) != nBufferBytes) {
712 fprintf(stderr, "WARNING: could not send Source caster request\n");
713 input_init = 0;
714 break;
715 }
716 nBufferBytes = 0;
717 /* check Source caster's response */
718 while (!init && nBufferBytes < (int) sizeof(szSendBuffer) &&
719 (nBufferBytes += recv(gps_socket, szSendBuffer, sizeof(szSendBuffer) - nBufferBytes, 0)) > 0) {
720 if( nBufferBytes > 17 && !strstr(szSendBuffer, "ICY 200 OK") && /* case 'proxy & ntrip 1.0 caster' */
721 (!strncmp(szSendBuffer, "HTTP/1.1 200 OK\r\n", 17) ||
722 !strncmp(szSendBuffer, "HTTP/1.0 200 OK\r\n", 17)) ) {
723 const char *datacheck = "Content-Type: gnss/data\r\n";
724 const char *chunkycheck = "Transfer-Encoding: chunked\r\n";
725 int l = strlen(datacheck)-1;
726 int j=0;
727 for(i = 0; j != l && i < nBufferBytes-l; ++i) {
728 for(j = 0; j < l && szSendBuffer[i+j] == datacheck[j]; ++j)
729 ;
[487]730 }
[9401]731 if(i == nBufferBytes-l) {
732 fprintf(stderr, "No 'Content-Type: gnss/data' found\n");
[487]733 input_init = 0;
[1570]734 }
[9401]735 l = strlen(chunkycheck)-1;
736 j=0;
737 for(i = 0; j != l && i < nBufferBytes-l; ++i) {
738 for(j = 0; j < l && szSendBuffer[i+j] == chunkycheck[j]; ++j)
739 ;
740 }
741 if(i < nBufferBytes-l)
742 chunkymode = 1;
743 init = 1;
[485]744 }
[9401]745 else if (strstr(szSendBuffer, "\r\n")) {
746 if (!strstr(szSendBuffer, "ICY 200 OK")) {
747 int k;
748 fprintf(stderr, "ERROR: could not get requested data from Source caster: ");
749 for (k = 0; k < nBufferBytes && szSendBuffer[k] != '\n' && szSendBuffer[k] != '\r'; ++k) {
750 fprintf(stderr, "%c", isprint(szSendBuffer[k]) ? szSendBuffer[k] : '.');
[485]751 }
[9401]752 fprintf(stderr, "\n");
753 if (!strstr(szSendBuffer, "SOURCETABLE 200 OK")) {
754 reconnect_sec_max = 0;
755 }
756 input_init = 0;
757 break;
758 } else {// eventuell auskommentieren!
759 init = 1;
[485]760 }
761 }
762
[9401]763 }
764 /* end input from NTRIP caster */
765
766 if (initfile && inputmode != SISNET) {
[487]767 char buffer[1024];
768 FILE *fh;
769 int i;
[485]770
[9401]771 if ((fh = fopen(initfile, "r"))) {
772 while ((i = fread(buffer, 1, sizeof(buffer), fh)) > 0) {
773 if ((send(gps_socket, buffer, (size_t) i, 0)) != i) {
[1570]774 perror("WARNING: sending init file");
775 input_init = 0;
776 break;
[487]777 }
[485]778 }
[9401]779 if (i < 0) {
[487]780 perror("ERROR: reading init file");
781 reconnect_sec_max = 0;
782 input_init = 0;
783 break;
784 }
785 fclose(fh);
[9401]786 } else {
[487]787 fprintf(stderr, "ERROR: can't read init file <%s>\n", initfile);
788 reconnect_sec_max = 0;
789 input_init = 0;
790 break;
[485]791 }
[1570]792 }
[485]793 }
[9401]794 if (inputmode == SISNET) {
795 int i, j;
796 char buffer[1024];
[485]797
[9401]798 i = snprintf(buffer, sizeof(buffer),
799 sisnet >= 30 ? "AUTH,%s,%s\r\n" : "AUTH,%s,%s", sisnetuser,
800 sisnetpassword);
801 if ((send(gps_socket, buffer, (size_t) i, 0)) != i) {
802 perror("WARNING: sending authentication for SISNeT data server");
803 input_init = 0;
804 break;
805 }
806 i = sisnet >= 30 ? 7 : 5;
807 if ((j = recv(gps_socket, buffer, i, 0)) != i
808 && strncmp("*AUTH", buffer, 5)) {
809 fprintf(stderr, "WARNING: SISNeT connect failed:");
810 for (i = 0; i < j; ++i) {
811 if (buffer[i] != '\r' && buffer[i] != '\n') {
812 fprintf(stderr, "%c", isprint(buffer[i]) ? buffer[i] : '.');
813 }
[487]814 }
[9401]815 fprintf(stderr, "\n");
[487]816 input_init = 0;
817 break;
818 }
[9401]819 if (sisnet >= 31) {
820 if ((send(gps_socket, "START\r\n", 7, 0)) != i) {
821 perror("WARNING: sending Sisnet start command");
822 input_init = 0;
823 break;
824 }
825 }
[1570]826 }
[9401]827 /*** receiver authentication ***/
828 if (recvrid && recvrpwd
829 && ((inputmode == TCPSOCKET) || (inputmode == UDPSOCKET))) {
830 if (strlen(recvrid) > (BUFSZ - 3)) {
831 fprintf(stderr, "ERROR: Receiver ID too long\n");
832 reconnect_sec_max = 0;
[487]833 input_init = 0;
834 break;
[9401]835 } else {
836 fprintf(stderr, "Sending user ID for receiver...\n");
837 nBufferBytes = recv(gps_socket, szSendBuffer, BUFSZ, 0);
838 strcpy(szSendBuffer, recvrid);
839 strcat(szSendBuffer, "\r\n");
840 if (send(gps_socket, szSendBuffer, strlen(szSendBuffer),
841 MSG_DONTWAIT) < 0) {
842 perror("WARNING: sending user ID for receiver");
843 input_init = 0;
844 break;
845 }
[485]846 }
[487]847
[9401]848 if (strlen(recvrpwd) > (BUFSZ - 3)) {
849 fprintf(stderr, "ERROR: Receiver password too long\n");
850 reconnect_sec_max = 0;
[487]851 input_init = 0;
852 break;
[9401]853 } else {
854 fprintf(stderr, "Sending user password for receiver...\n");
855 nBufferBytes = recv(gps_socket, szSendBuffer, BUFSZ, 0);
856 strcpy(szSendBuffer, recvrpwd);
857 strcat(szSendBuffer, "\r\n");
858 if (send(gps_socket, szSendBuffer, strlen(szSendBuffer),
859 MSG_DONTWAIT) < 0) {
860 perror("WARNING: sending user password for receiver");
861 input_init = 0;
862 break;
863 }
[487]864 }
[1570]865 }
[9401]866 break;
867 default:
868 usage(-1, argv[0]);
869 break;
[485]870 }
[1570]871
[487]872 /* ----- main part ----- */
[925]873 int output_init = 1, fallback = 0;
[487]874
[9401]875 while ((input_init) && (output_init)) {
[653]876#ifndef WINDOWSVERSION
[9401]877 if ((sigalarm_received) || (sigint_received) || (sigpipe_received))
878 break;
[653]879#else
880 if((sigalarm_received) || (sigint_received)) break;
881#endif
[9403]882
[9401]883 if (!(he = gethostbyname(outhost))) {
884 fprintf(stderr,
885 "ERROR: Destination caster, server or proxy host <%s> unknown\n",
886 outhost);
[915]887 close_session(casterouthost, mountpoint, session, rtsp_extension, 0);
[1570]888 usage(-2, argv[0]);
[9403]889 }else {fprintf(stderr,
890 "Destination caster, server or proxy host <%s> \n",
891 outhost);}
[485]892
[487]893 /* create socket */
[9401]894 if ((socket_tcp = socket(AF_INET, (outputmode == UDP ? SOCK_DGRAM : SOCK_STREAM), 0)) == INVALID_SOCKET) {
[1570]895 perror("ERROR: tcp socket");
[487]896 reconnect_sec_max = 0;
897 break;
[485]898 }
[9401]899 memset((char*) &caster, 0x00, sizeof(caster));
[487]900 memcpy(&caster.sin_addr, he->h_addr, (size_t)he->h_length);
901 caster.sin_family = AF_INET;
902 caster.sin_port = htons(outport);
[485]903
[9401]904 /* connect to Destination caster, server or proxy host */
[9403]905 fprintf(stderr, "caster|server output: host = %s, port = %d, mountpoint = %s"
[9401]906 ", mode = %s\n\n", inet_ntoa(caster.sin_addr), outport, mountpoint,
[9403]907 outputmode == NTRIP1 ? "ntrip1" :
908 outputmode == HTTP ? "http" :
909 outputmode == UDP ? "udp" :
[9448]910 outputmode == RTSP ? "rtsp" : "tcpip");
[485]911
[9401]912 if (connect(socket_tcp, (struct sockaddr*) &caster, sizeof(caster)) < 0) {
[9450]913 fprintf(stderr, "WARNING: can't connect output to %s at port %d",
[9401]914 inet_ntoa(caster.sin_addr), outport);
[9450]915 if (errno != EAGAIN) {perror(" ");} else {fprintf(stderr, "\n");}
[487]916 break;
917 }
[485]918
[487]919 /*** OutputMode handling ***/
[9401]920 switch (outputmode) {
921 case UDP: {
922 unsigned int session;
923 char rtpbuf[1526];
924 int i = 12, j;
[1570]925
[9401]926 udp_init = time(0);
927 srand(udp_init);
928 session = rand();
929 udp_tim = rand();
930 udp_seq = rand();
[1570]931
[9401]932 rtpbuf[0] = (2 << 6);
933 /* padding, extension, csrc are empty */
934 rtpbuf[1] = 97;
935 /* marker is empty */
936 rtpbuf[2] = (udp_seq >> 8) & 0xFF;
937 rtpbuf[3] = (udp_seq) & 0xFF;
938 rtpbuf[4] = (udp_tim >> 24) & 0xFF;
939 rtpbuf[5] = (udp_tim >> 16) & 0xFF;
940 rtpbuf[6] = (udp_tim >> 8) & 0xFF;
941 rtpbuf[7] = (udp_tim) & 0xFF;
942 /* sequence and timestamp are empty */
943 rtpbuf[8] = (session >> 24) & 0xFF;
944 rtpbuf[9] = (session >> 16) & 0xFF;
945 rtpbuf[10] = (session >> 8) & 0xFF;
946 rtpbuf[11] = (session) & 0xFF;
947 ++udp_seq;
[1570]948
[9401]949 j = snprintf(rtpbuf + i, sizeof(rtpbuf) - i - 40, /* leave some space for login */
950 "POST /%s HTTP/1.1\r\n"
951 "Host: %s\r\n"
952 "Ntrip-Version: Ntrip/2.0\r\n"
953 "User-Agent: %s/%s\r\n"
954 "Authorization: Basic %s%s%s\r\n"
955 "Connection: close\r\n"
956 "Transfer-Encoding: chunked\r\n\r\n", mountpoint, casterouthost,
957 AGENTSTRING, revisionstr, authorization,
958 ntrip_str ?
959 (outputmode == NTRIP1 ? "\r\nSTR: " : "\r\nNtrip-STR: ") : "",
960 ntrip_str);
961 i += j;
962 if (i > (int) sizeof(rtpbuf) - 40 || j < 0) /* second check for old glibc */
963 {
964 fprintf(stderr, "Requested data too long\n");
965 reconnect_sec_max = 0;
966 output_init = 0;
967 break;
968 } else {
969 rtpbuf[i++] = '\r';
970 rtpbuf[i++] = '\n';
971 rtpbuf[i++] = '\r';
972 rtpbuf[i++] = '\n';
973
974 if (send(socket_tcp, rtpbuf, i, 0) != i) {
975 perror("Could not send UDP packet");
[1570]976 reconnect_sec_max = 0;
977 output_init = 0;
978 break;
[9401]979 } else {
980 int stop = 0;
981 int numbytes;
982 if ((numbytes = recv(socket_tcp, rtpbuf, sizeof(rtpbuf) - 1, 0))
983 > 0) {
984 /* we don't expect message longer than 1513, so we cut the last
985 byte for security reasons to prevent buffer overrun */
986 rtpbuf[numbytes] = 0;
987 if (numbytes > 17 + 12
988 && (!strncmp(rtpbuf + 12, "HTTP/1.1 200 OK\r\n", 17)
989 || !strncmp(rtpbuf + 12, "HTTP/1.0 200 OK\r\n", 17))) {
990 const char *sessioncheck = "session: ";
991 int l = strlen(sessioncheck) - 1;
992 int j = 0;
993 for (i = 12; j != l && i < numbytes - l; ++i) {
994 for (j = 0;
995 j < l && tolower(rtpbuf[i + j]) == sessioncheck[j]; ++j)
996 ;
[1570]997 }
[9401]998 if (i != numbytes - l) /* found a session number */
[1570]999 {
[9401]1000 i += l;
1001 session = 0;
1002 while (i < numbytes && rtpbuf[i] >= '0' && rtpbuf[i] <= '9')
1003 session = session * 10 + rtpbuf[i++] - '0';
1004 if (rtpbuf[i] != '\r') {
1005 fprintf(stderr, "Could not extract session number\n");
1006 stop = 1;
[1570]1007 }
1008 }
[9401]1009 } else {
1010 int k;
1011 fprintf(stderr, "Could not access mountpoint: ");
1012 for (k = 12;
1013 k < numbytes && rtpbuf[k] != '\n' && rtpbuf[k] != '\r';
1014 ++k) {
1015 fprintf(stderr, "%c", isprint(rtpbuf[k]) ? rtpbuf[k] : '.');
1016 }
1017 fprintf(stderr, "\n");
1018 stop = 1;
[1570]1019 }
[9401]1020 }
1021 if (!stop) {
1022 send_receive_loop(socket_tcp, outputmode, NULL, 0, session, chunkymode);
1023 input_init = output_init = 0;
1024 /* send connection close always to allow nice session closing */
1025 udp_tim += (time(0) - udp_init) * 1000000 / TIME_RESOLUTION;
1026 rtpbuf[0] = (2 << 6);
1027 /* padding, extension, csrc are empty */
1028 rtpbuf[1] = 98;
1029 /* marker is empty */
1030 rtpbuf[2] = (udp_seq >> 8) & 0xFF;
1031 rtpbuf[3] = (udp_seq) & 0xFF;
1032 rtpbuf[4] = (udp_tim >> 24) & 0xFF;
1033 rtpbuf[5] = (udp_tim >> 16) & 0xFF;
1034 rtpbuf[6] = (udp_tim >> 8) & 0xFF;
1035 rtpbuf[7] = (udp_tim) & 0xFF;
1036 /* sequence and timestamp are empty */
1037 rtpbuf[8] = (session >> 24) & 0xFF;
1038 rtpbuf[9] = (session >> 16) & 0xFF;
1039 rtpbuf[10] = (session >> 8) & 0xFF;
1040 rtpbuf[11] = (session) & 0xFF;
[1570]1041
[9401]1042 send(socket_tcp, rtpbuf, 12, 0); /* cleanup */
1043 } else {
1044 reconnect_sec_max = 600;
1045 output_init = 0;
[1570]1046 }
1047 }
1048 }
[9401]1049 }
[1570]1050 break;
1051 case NTRIP1: /*** OutputMode Ntrip Version 1.0 ***/
[925]1052 fallback = 0;
[487]1053 nBufferBytes = snprintf(szSendBuffer, sizeof(szSendBuffer),
[9401]1054 "SOURCE %s %s/%s\r\n"
1055 "Source-Agent: %s/%s\r\n\r\n", password, post_extension,
1056 mountpoint, AGENTSTRING, revisionstr);
1057 if ((nBufferBytes > (int) sizeof(szSendBuffer))
1058 || (nBufferBytes < 0)) {
[487]1059 fprintf(stderr, "ERROR: Destination caster request to long\n");
1060 reconnect_sec_max = 0;
1061 output_init = 0;
1062 break;
[485]1063 }
[9401]1064 if (!send_to_caster(szSendBuffer, socket_tcp, nBufferBytes)) {
[487]1065 output_init = 0;
1066 break;
[485]1067 }
[487]1068 /* check Destination caster's response */
[9403]1069 nBufferBytes = recv(socket_tcp, szSendBuffer, sizeof(szSendBuffer), 0);
[487]1070 szSendBuffer[nBufferBytes] = '\0';
[9401]1071 if (!strstr(szSendBuffer, "OK")) {
[487]1072 char *a;
[485]1073 fprintf(stderr,
[9401]1074 "ERROR: Destination caster's or Proxy's reply is not OK: ");
1075 for (a = szSendBuffer; *a && *a != '\n' && *a != '\r'; ++a) {
[487]1076 fprintf(stderr, "%.1s", isprint(*a) ? a : ".");
1077 }
1078 fprintf(stderr, "\n");
[9401]1079 if ((strstr(szSendBuffer, "ERROR - Bad Password"))
1080 || (strstr(szSendBuffer, "400 Bad Request")))
1081 reconnect_sec_max = 0;
[487]1082 output_init = 0;
[485]1083 break;
1084 }
1085#ifndef NDEBUG
[9401]1086 else {
1087 fprintf(stderr, "Destination caster response:\n%s\n", szSendBuffer);
[487]1088 }
[485]1089#endif
[9401]1090 send_receive_loop(socket_tcp, outputmode, NULL, 0, 0, chunkymode);
[653]1091 input_init = output_init = 0;
[485]1092 break;
[1570]1093 case HTTP: /*** Ntrip-Version 2.0 HTTP/1.1 ***/
[487]1094 nBufferBytes = snprintf(szSendBuffer, sizeof(szSendBuffer),
[9401]1095 "POST %s/%s HTTP/1.1\r\n"
1096 "Host: %s\r\n"
1097 "Ntrip-Version: Ntrip/2.0\r\n"
1098 "User-Agent: %s/%s\r\n"
1099 "Authorization: Basic %s%s%s\r\n"
1100 "Connection: close\r\n"
1101 "Transfer-Encoding: chunked\r\n\r\n", post_extension,
1102 mountpoint, casterouthost, AGENTSTRING, revisionstr,
1103 authorization, ntrip_str ? "\r\nNtrip-STR: " : "", ntrip_str);
1104 if ((nBufferBytes > (int) sizeof(szSendBuffer))
1105 || (nBufferBytes < 0)) {
[487]1106 fprintf(stderr, "ERROR: Destination caster request to long\n");
1107 reconnect_sec_max = 0;
1108 output_init = 0;
1109 break;
1110 }
[9401]1111 if (!send_to_caster(szSendBuffer, socket_tcp, nBufferBytes)) {
[487]1112 output_init = 0;
1113 break;
1114 }
[485]1115 /* check Destination caster's response */
[9401]1116 nBufferBytes = recv(socket_tcp, szSendBuffer, sizeof(szSendBuffer), 0);
[485]1117 szSendBuffer[nBufferBytes] = '\0';
[9401]1118 if (!strstr(szSendBuffer, "HTTP/1.1 200 OK")) {
[485]1119 char *a;
[9401]1120 fprintf(stderr, "ERROR: Destination caster's%s reply is not OK: ",
1121 *proxyhost ? " or Proxy's" : "");
1122 for (a = szSendBuffer; *a && *a != '\n' && *a != '\r'; ++a) {
[487]1123 fprintf(stderr, "%.1s", isprint(*a) ? a : ".");
[485]1124 }
1125 fprintf(stderr, "\n");
1126 /* fallback if necessary */
[9401]1127 if (!strstr(szSendBuffer, "Ntrip-Version: Ntrip/2.0\r\n")) {
[487]1128 fprintf(stderr,
[9401]1129 " Ntrip Version 2.0 not implemented at Destination caster"
1130 " <%s>%s%s%s\n%s\n"
1131 "ntripserver falls back to Ntrip Version 1.0\n\n",
1132 casterouthost, *proxyhost ? " or Proxy <" : "", proxyhost,
1133 *proxyhost ? ">" : "",
1134 *proxyhost ?
1135 " or HTTP/1.1 not implemented at Proxy\n" : "");
[915]1136 close_session(casterouthost, mountpoint, session, rtsp_extension, 1);
[487]1137 outputmode = NTRIP1;
1138 break;
[9401]1139 } else if ((strstr(szSendBuffer, "HTTP/1.1 401 Unauthorized"))
1140 || (strstr(szSendBuffer, "501 Not Implemented"))) {
1141 reconnect_sec_max = 0;
[485]1142 }
[487]1143 output_init = 0;
1144 break;
[485]1145 }
1146#ifndef NDEBUG
[9401]1147 else {
1148 fprintf(stderr, "Destination caster response:\n%s\n", szSendBuffer);
[485]1149 }
1150#endif
[9401]1151 send_receive_loop(socket_tcp, outputmode, NULL, 0, 0, chunkymode);
[653]1152 input_init = output_init = 0;
[487]1153 break;
[1570]1154 case RTSP: /*** Ntrip-Version 2.0 RTSP / RTP ***/
[9401]1155 if ((socket_udp = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
[487]1156 perror("ERROR: udp socket");
1157 exit(4);
1158 }
1159 /* fill structure with local address information for UDP */
1160 memset(&local, 0, sizeof(local));
1161 local.sin_family = AF_INET;
1162 local.sin_port = htons(0);
1163 local.sin_addr.s_addr = htonl(INADDR_ANY);
[9401]1164 len = (socklen_t) sizeof(local);
[487]1165 /* bind() in order to get a random RTP client_port */
[9401]1166 if ((bind(socket_udp, (struct sockaddr*) &local, len)) < 0) {
[487]1167 perror("ERROR: udp bind");
1168 reconnect_sec_max = 0;
1169 output_init = 0;
1170 break;
1171 }
[9448]1172 if ((getsockname(socket_udp, (struct sockaddr*) &local, &len)) != -1) {
[9401]1173 client_port = (unsigned int) ntohs(local.sin_port);
1174 } else {
[487]1175 perror("ERROR: getsockname(localhost)");
1176 reconnect_sec_max = 0;
1177 output_init = 0;
1178 break;
1179 }
1180 nBufferBytes = snprintf(szSendBuffer, sizeof(szSendBuffer),
[9401]1181 "SETUP rtsp://%s%s/%s RTSP/1.0\r\n"
1182 "CSeq: %d\r\n"
1183 "Ntrip-Version: Ntrip/2.0\r\n"
1184 "Ntrip-Component: Ntripserver\r\n"
1185 "User-Agent: %s/%s\r\n"
1186 "Transport: RTP/GNSS;unicast;client_port=%u\r\n"
1187 "Authorization: Basic %s%s%s\r\n\r\n", casterouthost,
1188 rtsp_extension, mountpoint, udp_cseq++, AGENTSTRING, revisionstr,
1189 client_port, authorization, ntrip_str ? "\r\nNtrip-STR: " : "",
1190 ntrip_str);
1191 if ((nBufferBytes > (int) sizeof(szSendBuffer))
1192 || (nBufferBytes < 0)) {
[487]1193 fprintf(stderr, "ERROR: Destination caster request to long\n");
1194 reconnect_sec_max = 0;
1195 output_init = 0;
1196 break;
1197 }
[9401]1198 if (!send_to_caster(szSendBuffer, socket_tcp, nBufferBytes)) {
[487]1199 output_init = 0;
1200 break;
1201 }
[9401]1202 while ((nBufferBytes = recv(socket_tcp, szSendBuffer,
1203 sizeof(szSendBuffer), 0)) > 0) {
[487]1204 /* check Destination caster's response */
1205 szSendBuffer[nBufferBytes] = '\0';
[9401]1206 if (!strstr(szSendBuffer, "RTSP/1.0 200 OK")) {
[487]1207 char *a;
[9401]1208 fprintf(stderr, "ERROR: Destination caster's%s reply is not OK: ",
1209 *proxyhost ? " or Proxy's" : "");
1210 for (a = szSendBuffer; *a && *a != '\n' && *a != '\r'; ++a) {
[1570]1211 fprintf(stderr, "%c", isprint(*a) ? *a : '.');
[487]1212 }
1213 fprintf(stderr, "\n");
1214 /* fallback if necessary */
[9401]1215 if (strncmp(szSendBuffer, "RTSP", 4) != 0) {
1216 if (strstr(szSendBuffer, "Ntrip-Version: Ntrip/2.0\r\n")) {
[487]1217 fprintf(stderr,
[9401]1218 " RTSP not implemented at Destination caster <%s>%s%s%s\n\n"
1219 "ntripserver falls back to Ntrip Version 2.0 in TCP/IP"
1220 " mode\n\n", casterouthost,
1221 *proxyhost ? " or Proxy <" : "", proxyhost,
1222 *proxyhost ? ">" : "");
1223 close_session(casterouthost, mountpoint, session,
1224 rtsp_extension, 1);
[487]1225 outputmode = HTTP;
[925]1226 fallback = 1;
[487]1227 break;
[9401]1228 } else {
[487]1229 fprintf(stderr,
[9401]1230 " Ntrip-Version 2.0 not implemented at Destination caster"
1231 "<%s>%s%s%s\n%s"
1232 " or RTSP/1.0 not implemented at Destination caster%s\n\n"
1233 "ntripserver falls back to Ntrip Version 1.0\n\n",
1234 casterouthost, *proxyhost ? " or Proxy <" : "", proxyhost,
1235 *proxyhost ? ">" : "",
1236 *proxyhost ?
1237 " or HTTP/1.1 not implemented at Proxy\n" : "",
1238 *proxyhost ? " or Proxy" : "");
1239 close_session(casterouthost, mountpoint, session,
1240 rtsp_extension, 1);
[487]1241 outputmode = NTRIP1;
[925]1242 fallback = 1;
[487]1243 break;
[1570]1244 }
[9401]1245 } else if ((strstr(szSendBuffer, "RTSP/1.0 401 Unauthorized"))
1246 || (strstr(szSendBuffer, "RTSP/1.0 501 Not Implemented"))) {
[487]1247 reconnect_sec_max = 0;
1248 }
1249 output_init = 0;
1250 break;
[485]1251 }
[487]1252#ifndef NDEBUG
[9401]1253 else {
1254 fprintf(stderr, "Destination caster response:\n%s\n", szSendBuffer);
[485]1255 }
[487]1256#endif
[9401]1257 if ((strstr(szSendBuffer, "RTSP/1.0 200 OK\r\n"))
1258 && (strstr(szSendBuffer, "CSeq: 1\r\n"))) {
1259 for (token = strtok(szSendBuffer, dlim); token != NULL; token =
1260 strtok(NULL, dlim)) {
1261 tok_buf[i] = token;
1262 i++;
[487]1263 }
1264 session = atoi(tok_buf[6]);
1265 server_port = atoi(tok_buf[10]);
1266 nBufferBytes = snprintf(szSendBuffer, sizeof(szSendBuffer),
[9401]1267 "RECORD rtsp://%s%s/%s RTSP/1.0\r\n"
1268 "CSeq: %d\r\n"
1269 "Session: %u\r\n"
1270 "\r\n", casterouthost, rtsp_extension, mountpoint,
1271 udp_cseq++, session);
1272 if ((nBufferBytes >= (int) sizeof(szSendBuffer))
1273 || (nBufferBytes < 0)) {
1274 fprintf(stderr, "ERROR: Destination caster request to long\n");
[487]1275 reconnect_sec_max = 0;
1276 output_init = 0;
[1570]1277 break;
[487]1278 }
[9401]1279 if (!send_to_caster(szSendBuffer, socket_tcp, nBufferBytes)) {
[653]1280 output_init = 0;
[9401]1281 break;
[487]1282 }
[9401]1283 } else if ((strstr(szSendBuffer, "RTSP/1.0 200 OK\r\n"))
1284 && (strstr(szSendBuffer, "CSeq: 2\r\n"))) {
[487]1285 /* fill structure with caster address information for UDP */
1286 memset(&casterRTP, 0, sizeof(casterRTP));
1287 casterRTP.sin_family = AF_INET;
[9401]1288 casterRTP.sin_port = htons(((uint16_t) server_port));
1289 if ((he = gethostbyname(outhost)) == NULL) {
[1570]1290 fprintf(stderr, "ERROR: Destination caster unknown\n");
[487]1291 reconnect_sec_max = 0;
1292 output_init = 0;
1293 break;
[9401]1294 } else {
1295 memcpy((char*) &casterRTP.sin_addr.s_addr, he->h_addr_list[0],
1296 (size_t) he->h_length);
[487]1297 }
[9401]1298 len = (socklen_t) sizeof(casterRTP);
1299 send_receive_loop(socket_udp, outputmode,
1300 (struct sockaddr*) &casterRTP, (socklen_t) len, session, chunkymode);
[487]1301 break;
[9401]1302 } else {
1303 break;
[485]1304 }
[487]1305 }
[653]1306 input_init = output_init = 0;
[487]1307 break;
[9448]1308 case TCPIP:
[9403]1309 fallback = 0;
1310 send_receive_loop(socket_tcp, outputmode, NULL, 0, 0, chunkymode);
1311 input_init = output_init = 0;
[9401]1312 break;
[765]1313 }
[485]1314 }
[915]1315 close_session(casterouthost, mountpoint, session, rtsp_extension, 0);
[9401]1316 if ((reconnect_sec_max || fallback) && !sigint_received)
[653]1317 reconnect_sec = reconnect(reconnect_sec, reconnect_sec_max);
[9401]1318 else
1319 inputmode = LAST;
[485]1320 }
1321 return 0;
1322}
1323
[9401]1324static void send_receive_loop(sockettype sock, int outmode,
1325 struct sockaddr *pcasterRTP, socklen_t length, unsigned int rtpssrc,
1326 int chunkymode) {
1327 int nodata = 0;
1328 char buffer[BUFSZ] = { 0 };
1329 char sisnetbackbuffer[200];
1330 char szSendBuffer[BUFSZ] = "";
1331 int nBufferBytes = 0;
1332 int remainChunk = 0;
[485]1333
[9401]1334 /* RTSP / RTP Mode */
1335 int isfirstpacket = 1;
1336 struct timeval now;
1337 struct timeval last = { 0, 0 };
[485]1338 long int sendtimediff;
[9401]1339 int rtpseq = 0;
1340 int rtptime = 0;
1341 time_t laststate = time(0);
[485]1342
[9401]1343 if (outmode == UDP) {
[1570]1344 rtptime = time(0);
[915]1345#ifdef WINDOWSVERSION
1346 u_long blockmode = 1;
1347 if(ioctlsocket(socket_tcp, FIONBIO, &blockmode))
1348#else /* WINDOWSVERSION */
[9401]1349 if (fcntl(socket_tcp, F_SETFL, O_NONBLOCK) < 0)
[915]1350#endif /* WINDOWSVERSION */
[9401]1351 {
[915]1352 fprintf(stderr, "Could not set nonblocking mode\n");
1353 return;
1354 }
[9401]1355 } else if (outmode == RTSP) {
[1570]1356#ifdef WINDOWSVERSION
1357 u_long blockmode = 1;
1358 if(ioctlsocket(socket_tcp, FIONBIO, &blockmode))
1359#else /* WINDOWSVERSION */
[9401]1360 if (fcntl(socket_tcp, F_SETFL, O_NONBLOCK) < 0)
[1570]1361#endif /* WINDOWSVERSION */
[9401]1362 {
[1570]1363 fprintf(stderr, "Could not set nonblocking mode\n");
1364 return;
1365 }
1366 }
[915]1367
[485]1368 /* data transmission */
[9401]1369 fprintf(stderr, "transfering data ...\n");
1370 int send_recv_success = 0;
[1570]1371#ifdef WINDOWSVERSION
1372 time_t nodata_begin = 0, nodata_current = 0;
[653]1373#endif
[9401]1374 while (1) {
1375 if (send_recv_success < 3)
1376 send_recv_success++;
1377 if (!nodata) {
[653]1378#ifndef WINDOWSVERSION
1379 alarm(ALARMTIME);
1380#else
1381 time(&nodata_begin);
1382#endif
[9401]1383 } else {
[1570]1384 nodata = 0;
1385#ifdef WINDOWSVERSION
[653]1386 time(&nodata_current);
[9401]1387 if(difftime(nodata_current, nodata_begin) >= ALARMTIME) {
[653]1388 sigalarm_received = 1;
1389 fprintf(stderr, "ERROR: more than %d seconds no activity\n", ALARMTIME);
1390 }
1391#endif
1392 }
[487]1393 /* signal handling*/
[653]1394#ifdef WINDOWSVERSION
1395 if((sigalarm_received) || (sigint_received)) break;
1396#else
[9401]1397 if ((sigalarm_received) || (sigint_received) || (sigpipe_received))
1398 break;
[653]1399#endif
[9401]1400 if (!nBufferBytes) {
1401 if (inputmode == SISNET && sisnet <= 30) {
[485]1402 int i;
1403 /* a somewhat higher rate than 1 second to get really each block */
1404 /* means we need to skip double blocks sometimes */
[9401]1405 struct timeval tv = { 0, 700000 };
[485]1406 select(0, 0, 0, 0, &tv);
1407 memcpy(sisnetbackbuffer, buffer, sizeof(sisnetbackbuffer));
1408 i = (sisnet >= 30 ? 5 : 3);
[9401]1409 if ((send(gps_socket, "MSG\r\n", i, 0)) != i) {
[487]1410 perror("WARNING: sending SISNeT data request failed");
1411 return;
[485]1412 }
1413 }
[9403]1414 /***********************/
1415 /* receiving data */
1416 /***********************/
1417
1418 /* INFILE */
[9401]1419 if (inputmode == INFILE)
[653]1420 nBufferBytes = read(gps_file, buffer, sizeof(buffer));
[9403]1421
1422 /* SERIAL */
[9401]1423 else if (inputmode == SERIAL) {
[1570]1424#ifndef WINDOWSVERSION
[653]1425 nBufferBytes = read(gps_serial, buffer, sizeof(buffer));
1426#else
[1570]1427 DWORD nRead = 0;
[653]1428 if(!ReadFile(gps_serial, buffer, sizeof(buffer), &nRead, NULL))
1429 {
1430 fprintf(stderr,"ERROR: reading serial input failed\n");
1431 return;
1432 }
1433 nBufferBytes = (int)nRead;
1434#endif
1435 }
[9403]1436
1437 /* ALL OTHER MODES */
[768]1438 else
1439#ifdef WINDOWSVERSION
[653]1440 nBufferBytes = recv(gps_socket, buffer, sizeof(buffer), 0);
[768]1441#else
1442 nBufferBytes = read(gps_socket, buffer, sizeof(buffer));
1443#endif
[9401]1444 if (!nBufferBytes) {
[653]1445 fprintf(stderr, "WARNING: no data received from input\n");
1446 nodata = 1;
1447#ifndef WINDOWSVERSION
[485]1448 sleep(3);
[653]1449#else
1450 Sleep(3*1000);
1451#endif
[485]1452 continue;
[9401]1453 } else if ((nBufferBytes < 0) && (!sigint_received)) {
[487]1454 perror("WARNING: reading input failed");
1455 return;
[485]1456 }
1457 /* we can compare the whole buffer, as the additional bytes
[9401]1458 remain unchanged */
1459 if (inputmode == SISNET && sisnet <= 30
1460 && !memcmp(sisnetbackbuffer, buffer, sizeof(sisnetbackbuffer))) {
[485]1461 nBufferBytes = 0;
1462 }
1463 }
[9401]1464 if (nBufferBytes < 0)
[1570]1465 return;
[9401]1466
1467 if (chunkymode) {
1468 int cstop = 0;
1469 int pos = 0;
1470 int chunksize = 0;
1471 int totalbytes = 0;
1472 int nChunkBytes = 0;
1473 char chunkBytes[BUFSZ];
1474
[9402]1475 long i;
[9401]1476 while (!sigint_received && !cstop && pos < nBufferBytes) {
1477 switch (chunkymode) {
1478 case 1: /* reading number starts */
1479 chunksize = 0;
1480 ++chunkymode; /* no break */
1481 break;
1482 case 2: /* during reading number */
1483 i = buffer[pos++];
1484 if (i >= '0' && i <= '9')
1485 chunksize = chunksize * 16 + i - '0';
1486 else if (i >= 'a' && i <= 'f')
1487 chunksize = chunksize * 16 + i - 'a' + 10;
1488 else if (i >= 'A' && i <= 'F')
1489 chunksize = chunksize * 16 + i - 'A' + 10;
1490 else if (i == '\r')
1491 ++chunkymode;
1492 else if (i == ';')
1493 chunkymode = 5;
1494 else
1495 cstop = 1;
1496 break;
1497 case 3: /* scanning for return */
1498 if (buffer[pos++] == '\n')
1499 chunkymode = chunksize ? 4 : 1;
1500 else
1501 cstop = 1;
1502 break;
1503 case 4: /* output data */
1504 i = nBufferBytes - pos;
1505 if (i > chunksize) {
1506 i = chunksize;
1507 }
1508 if (nChunkBytes <= nBufferBytes) {
1509 memcpy(chunkBytes + nChunkBytes, buffer + pos, (size_t) i);
1510 nChunkBytes += i;
1511 }
1512 totalbytes += i;
1513 chunksize -= i;
1514 pos += i;
1515 if (!chunksize)
1516 chunkymode = 1;
1517 break;
1518 case 5:
1519 if (i == '\r')
1520 chunkymode = 3;
1521 break;
1522 }
1523 }
1524 if (cstop) {
1525 fprintf(stderr, "Error in chunky transfer encoding\n");
1526 return;
1527 }
1528 else {
1529 if (nChunkBytes <= nBufferBytes) {
1530 strcpy(buffer, "");
1531 memcpy(buffer, chunkBytes, (size_t) nChunkBytes);
1532 nBufferBytes = nChunkBytes;
1533 }
1534 }
1535 }
[9403]1536
1537 /*****************/
1538 /* send data */
1539 /*****************/
[9448]1540 if ((nBufferBytes) && (outmode == NTRIP1 || outmode == TCPIP)) {
[485]1541 int i;
[9401]1542 if ((i = send(sock, buffer, (size_t) nBufferBytes, MSG_DONTWAIT)) != nBufferBytes) {
1543 if (i < 0) {
1544 if (errno != EAGAIN) {
[487]1545 perror("WARNING: could not send data to Destination caster");
[485]1546 return;
1547 }
[9401]1548 } else if (i) {
1549 memmove(buffer, buffer + i, (size_t) (nBufferBytes - i));
[485]1550 nBufferBytes -= i;
1551 }
[9401]1552 } else {
[485]1553 nBufferBytes = 0;
1554 }
[9401]1555 } else if ((nBufferBytes) && (outmode == UDP)) {
[1570]1556 char rtpbuf[1592];
1557 int i;
1558 int ct = time(0);
[9401]1559 udp_tim += (ct - udp_init) * 1000000 / TIME_RESOLUTION;
[1570]1560 udp_init = ct;
[9401]1561 rtpbuf[0] = (2 << 6);
[1570]1562 rtpbuf[1] = 96;
[9401]1563 rtpbuf[2] = (udp_seq >> 8) & 0xFF;
1564 rtpbuf[3] = (udp_seq) & 0xFF;
1565 rtpbuf[4] = (udp_tim >> 24) & 0xFF;
1566 rtpbuf[5] = (udp_tim >> 16) & 0xFF;
1567 rtpbuf[6] = (udp_tim >> 8) & 0xFF;
1568 rtpbuf[7] = (udp_tim) & 0xFF;
1569 rtpbuf[8] = (rtpssrc >> 24) & 0xFF;
1570 rtpbuf[9] = (rtpssrc >> 16) & 0xFF;
1571 rtpbuf[10] = (rtpssrc >> 8) & 0xFF;
1572 rtpbuf[11] = (rtpssrc) & 0xFF;
[1570]1573 ++udp_seq;
[9401]1574 memcpy(rtpbuf + 12, buffer, nBufferBytes);
1575 if ((i = send(socket_tcp, rtpbuf, (size_t) nBufferBytes + 12, MSG_DONTWAIT)) != nBufferBytes + 12) {
1576 if (errno != EAGAIN) {
[1570]1577 perror("WARNING: could not send data to Destination caster");
1578 return;
1579 }
[9401]1580 } else
[1570]1581 nBufferBytes = 0;
1582 i = recv(socket_tcp, rtpbuf, sizeof(rtpbuf), 0);
[9401]1583 if (i >= 12 && (unsigned char) rtpbuf[0] == (2 << 6)
1584 && rtpssrc
1585 == (unsigned int) (((unsigned char) rtpbuf[8] << 24)
1586 + ((unsigned char) rtpbuf[9] << 16)
1587 + ((unsigned char) rtpbuf[10] << 8)
1588 + (unsigned char) rtpbuf[11])) {
1589 if (rtpbuf[1] == 96)
1590 rtptime = time(0);
1591 else if (rtpbuf[1] == 98) {
1592 fprintf(stderr, "Connection end\n");
1593 return;
[1573]1594 }
[9401]1595 } else if (time(0) > rtptime + 60) {
[1570]1596 fprintf(stderr, "Timeout\n");
1597 return;
1598 }
1599 }
[485]1600 /*** Ntrip-Version 2.0 HTTP/1.1 ***/
[9401]1601 else if ((nBufferBytes) && (outmode == HTTP)) {
1602 if (!remainChunk) {
1603 int nChunkBytes = snprintf(szSendBuffer, sizeof(szSendBuffer), "%x\r\n", nBufferBytes);
[8788]1604 send(sock, szSendBuffer, nChunkBytes, 0);
1605 remainChunk = nBufferBytes;
[8784]1606 }
[9401]1607 int i = send(sock, buffer, (size_t) remainChunk, MSG_DONTWAIT);
1608 if (i < 0) {
1609 if (errno != EAGAIN) {
[8788]1610 perror("WARNING: could not send data to Destination caster");
1611 return;
[485]1612 }
[9401]1613 } else if (i) {
1614 memmove(buffer, buffer + i, (size_t) (nBufferBytes - i));
[8788]1615 nBufferBytes -= i;
1616 remainChunk -= i;
[9401]1617 } else {
1618 nBufferBytes = 0;
1619 remainChunk = 0;
[8788]1620 }
[9401]1621 if (!remainChunk)
[8787]1622 send(sock, "\r\n", strlen("\r\n"), 0);
[485]1623 }
1624 /*** Ntrip-Version 2.0 RTSP(TCP) / RTP(UDP) ***/
[9401]1625 else if ((nBufferBytes) && (outmode == RTSP)) {
[915]1626 time_t ct;
1627 int r;
[9401]1628 char rtpbuffer[BUFSZ + 12];
[485]1629 int i, j;
1630 gettimeofday(&now, NULL);
1631 /* RTP data packet generation*/
[9401]1632 if (isfirstpacket) {
[485]1633 rtpseq = rand();
1634 rtptime = rand();
1635 last = now;
1636 isfirstpacket = 0;
[9401]1637 } else {
[485]1638 ++rtpseq;
[9401]1639 sendtimediff = (((now.tv_sec - last.tv_sec) * 1000000)
1640 + (now.tv_usec - last.tv_usec));
1641 rtptime += sendtimediff / TIME_RESOLUTION;
[485]1642 }
[9401]1643 rtpbuffer[0] = (RTP_VERSION << 6);
[485]1644 /* padding, extension, csrc are empty */
1645 rtpbuffer[1] = 96;
1646 /* marker is empty */
[9401]1647 rtpbuffer[2] = rtpseq >> 8;
[485]1648 rtpbuffer[3] = rtpseq;
[9401]1649 rtpbuffer[4] = rtptime >> 24;
1650 rtpbuffer[5] = rtptime >> 16;
1651 rtpbuffer[6] = rtptime >> 8;
[485]1652 rtpbuffer[7] = rtptime;
[9401]1653 rtpbuffer[8] = rtpssrc >> 24;
1654 rtpbuffer[9] = rtpssrc >> 16;
1655 rtpbuffer[10] = rtpssrc >> 8;
[485]1656 rtpbuffer[11] = rtpssrc;
[9401]1657 for (j = 0; j < nBufferBytes; j++) {
1658 rtpbuffer[12 + j] = buffer[j];
1659 }
1660 last.tv_sec = now.tv_sec;
[485]1661 last.tv_usec = now.tv_usec;
[9401]1662 if ((i = sendto(sock, rtpbuffer, 12 + nBufferBytes, 0, pcasterRTP, length))
1663 != (nBufferBytes + 12)) {
1664 if (i < 0) {
1665 if (errno != EAGAIN) {
[487]1666 perror("WARNING: could not send data to Destination caster");
[485]1667 return;
1668 }
[9401]1669 } else if (i) {
1670 memmove(buffer, buffer + (i - 12),
1671 (size_t) (nBufferBytes - (i - 12)));
1672 nBufferBytes -= i - 12;
[485]1673 }
[9401]1674 } else {
[485]1675 nBufferBytes = 0;
1676 }
[915]1677 ct = time(0);
[9401]1678 if (ct - laststate > 15) {
[915]1679 i = snprintf(buffer, sizeof(buffer),
[9401]1680 "GET_PARAMETER rtsp://%s%s/%s RTSP/1.0\r\n"
1681 "CSeq: %d\r\n"
1682 "Session: %u\r\n"
1683 "\r\n", casterouthost, rtsp_extension, mountpoint, udp_cseq++,
1684 rtpssrc);
1685 if (i > (int) sizeof(buffer) || i < 0) {
[915]1686 fprintf(stderr, "Requested data too long\n");
1687 return;
[9401]1688 } else if (send(socket_tcp, buffer, (size_t) i, 0) != i) {
[915]1689 perror("send");
1690 return;
1691 }
1692 laststate = ct;
1693 }
1694 /* ignore RTSP server replies */
[9401]1695 if ((r = recv(socket_tcp, buffer, sizeof(buffer), 0)) < 0) {
[915]1696#ifdef WINDOWSVERSION
1697 if(WSAGetLastError() != WSAEWOULDBLOCK)
1698#else /* WINDOWSVERSION */
[9401]1699 if (errno != EAGAIN)
[915]1700#endif /* WINDOWSVERSION */
1701 {
1702 fprintf(stderr, "Control connection closed\n");
1703 return;
1704 }
[9401]1705 } else if (!r) {
[915]1706 fprintf(stderr, "Control connection read error\n");
1707 return;
1708 }
[485]1709 }
[9448]1710 else if ((nBufferBytes) && (outmode == TCPIP)) {
1711 int i;
1712 if ((i = send(sock, buffer, (size_t) nBufferBytes, MSG_DONTWAIT)) != nBufferBytes) {
1713 if (i < 0) {
1714 if (errno != EAGAIN) {
1715 perror("WARNING: could not send data to IP server and port");
1716 return;
1717 }
1718 } else if (i) {
1719 memmove(buffer, buffer + i, (size_t) (nBufferBytes - i));
1720 nBufferBytes -= i;
1721 }
1722 } else {
1723 nBufferBytes = 0;
1724 }
[9403]1725 }
[9401]1726 if (send_recv_success == 3)
1727 reconnect_sec = 1;
[485]1728 }
1729 return;
1730}
1731
1732/********************************************************************
1733 * openserial
1734 *
1735 * Open the serial port with the given device name and configure it for
1736 * reading NMEA data from a GPS receiver.
1737 *
1738 * Parameters:
1739 * tty : pointer to : A zero-terminated string containing the device
1740 * unsigned char name of the appropriate serial port.
[653]1741 * blocksz : integer : Block size for port I/O (ifndef WINDOWSVERSION)
[485]1742 * baud : integer : Baud rate for port I/O
1743 *
1744 * Return Value:
1745 * The function returns a file descriptor for the opened port if successful.
[653]1746 * The function returns -1 / INVALID_HANDLE_VALUE in the event of an error.
[485]1747 *
1748 * Remarks:
1749 *
1750 ********************************************************************/
[653]1751#ifndef WINDOWSVERSION
[9401]1752static int openserial(const char *tty, int blocksz, int baud) {
[485]1753 struct termios termios;
1754
[9401]1755 /*** opening the serial port ***/
[653]1756 gps_serial = open(tty, O_RDWR | O_NONBLOCK | O_EXLOCK);
[9401]1757 if (gps_serial < 0) {
[485]1758 perror("ERROR: opening serial connection");
1759 return (-1);
1760 }
[653]1761
[9401]1762 /*** configuring the serial port ***/
1763 if (tcgetattr(gps_serial, &termios) < 0) {
[485]1764 perror("ERROR: get serial attributes");
1765 return (-1);
1766 }
1767 termios.c_iflag = 0;
[9401]1768 termios.c_oflag = 0; /* (ONLRET) */
[485]1769 termios.c_cflag = CS8 | CLOCAL | CREAD;
1770 termios.c_lflag = 0;
1771 {
1772 int cnt;
[9401]1773 for (cnt = 0; cnt < NCCS; cnt++)
[485]1774 termios.c_cc[cnt] = -1;
1775 }
1776 termios.c_cc[VMIN] = blocksz;
1777 termios.c_cc[VTIME] = 2;
1778
1779#if (B4800 != 4800)
[9401]1780 /* Not every system has speed settings equal to absolute speed value. */
1781 switch (baud) {
1782 case 300:
1783 baud = B300;
1784 break;
1785 case 1200:
1786 baud = B1200;
1787 break;
1788 case 2400:
1789 baud = B2400;
1790 break;
1791 case 4800:
1792 baud = B4800;
1793 break;
1794 case 9600:
1795 baud = B9600;
1796 break;
1797 case 19200:
1798 baud = B19200;
1799 break;
1800 case 38400:
1801 baud = B38400;
1802 break;
[485]1803#ifdef B57600
[9401]1804 case 57600:
1805 baud = B57600;
1806 break;
[485]1807#endif
1808#ifdef B115200
[9401]1809 case 115200:
1810 baud = B115200;
1811 break;
[485]1812#endif
1813#ifdef B230400
[9401]1814 case 230400:
1815 baud = B230400;
1816 break;
[485]1817#endif
[9401]1818 default:
1819 fprintf(stderr, "WARNING: Baud settings not useful, using 19200\n");
1820 baud = B19200;
1821 break;
[485]1822 }
1823#endif
1824
[9401]1825 if (cfsetispeed(&termios, baud) != 0) {
[485]1826 perror("ERROR: setting serial speed with cfsetispeed");
1827 return (-1);
1828 }
[9401]1829 if (cfsetospeed(&termios, baud) != 0) {
[485]1830 perror("ERROR: setting serial speed with cfsetospeed");
1831 return (-1);
1832 }
[9401]1833 if (tcsetattr(gps_serial, TCSANOW, &termios) < 0) {
[485]1834 perror("ERROR: setting serial attributes");
1835 return (-1);
1836 }
[9401]1837 if (fcntl(gps_serial, F_SETFL, 0) == -1) {
[485]1838 perror("WARNING: setting blocking inputmode failed");
1839 }
[653]1840 return (gps_serial);
1841}
1842#else
[9401]1843static HANDLE openserial(const char * tty, int baud) {
[654]1844 char compath[15] = "";
[653]1845
[654]1846 snprintf(compath, sizeof(compath), "\\\\.\\%s", tty);
[2273]1847 if((gps_serial = CreateFile(compath, GENERIC_WRITE|GENERIC_READ
[9401]1848 , 0, 0, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
[653]1849 fprintf(stderr, "ERROR: opening serial connection\n");
1850 return (INVALID_HANDLE_VALUE);
1851 }
[2273]1852
1853 DCB dcb;
1854 memset(&dcb, 0, sizeof(dcb));
1855 char str[100];
1856 snprintf(str,sizeof(str),
1857 "baud=%d parity=N data=8 stop=1 xon=off octs=off rts=off",
1858 baud);
1859
1860 COMMTIMEOUTS ct = {1000, 1, 0, 0, 0};
1861
[9401]1862 if(!BuildCommDCB(str, &dcb)) {
[653]1863 fprintf(stderr, "ERROR: get serial attributes\n");
1864 return (INVALID_HANDLE_VALUE);
1865 }
[9401]1866 else if(!SetCommState(gps_serial, &dcb)) {
[2273]1867 fprintf(stderr, "ERROR: set serial attributes\n");
[653]1868 return (INVALID_HANDLE_VALUE);
1869 }
[9401]1870 else if(!SetCommTimeouts(gps_serial, &ct)) {
[653]1871 fprintf(stderr, "ERROR: set serial timeouts\n");
[1570]1872 return (INVALID_HANDLE_VALUE);
[653]1873 }
[2273]1874
[653]1875 return (gps_serial);
[2273]1876}
[653]1877#endif
[485]1878
1879/********************************************************************
[9401]1880 * usage
1881 *
1882 * Send a usage message to standard error and quit the program.
1883 *
1884 * Parameters:
1885 * None.
1886 *
1887 * Return Value:
1888 * The function does not return a value.
1889 *
1890 * Remarks:
1891 *
1892 *********************************************************************/
[485]1893#ifdef __GNUC__
1894__attribute__ ((noreturn))
1895#endif /* __GNUC__ */
[9401]1896void usage(int rc, char *name) {
[485]1897 fprintf(stderr, "Version %s (%s) GPL" COMPILEDATE "\nUsage:\n%s [OPTIONS]\n",
[9401]1898 revisionstr, datestr, name);
[485]1899 fprintf(stderr, "PURPOSE\n");
[9401]1900 fprintf(stderr,
1901 " The purpose of this program is to pick up a GNSS data stream (Input, Source)\n");
[485]1902 fprintf(stderr, " from either\n\n");
1903 fprintf(stderr, " 1. a Serial port, or\n");
1904 fprintf(stderr, " 2. an IP server, or\n");
1905 fprintf(stderr, " 3. a File, or\n");
1906 fprintf(stderr, " 4. a SISNeT Data Server, or\n");
1907 fprintf(stderr, " 5. a UDP server, or\n");
[9401]1908 fprintf(stderr, " 6. an NTRIP Version 1.0 Caster\n");
1909 fprintf(stderr, " 7. an NTRIP Version 2.0 Caster in HTTP mode \n\n");
1910 fprintf(stderr,
1911 " and forward that incoming stream (Output, Destination) to either\n\n");
[9448]1912 fprintf(stderr, " 1. an NTRIP Version 2.0 Caster via TCP/IP (Output, Destination), or\n");
1913 fprintf(stderr, " 2. an NTRIP Version 2.0 Caster via RTSP/RTP (Output, Destination), or\n");
1914 fprintf(stderr, " 3. an NTRIP Version 2.0 Caster via plain UDP (Output, Destination), or\n");
1915 fprintf(stderr, " 4. an NTRIP Version 1.0 Caster, or\n");
1916 fprintf(stderr, " 5. an IP server via TCP/IP\n\n\n");
[485]1917 fprintf(stderr, "OPTIONS\n");
1918 fprintf(stderr, " -h|? print this help screen\n\n");
[9403]1919 fprintf(stderr, " -E <ProxyHost> Proxy server host name or address, required i.e. when\n");
1920 fprintf(stderr, " running the program in a proxy server protected LAN,\n");
[485]1921 fprintf(stderr, " optional\n");
[9403]1922 fprintf(stderr, " -F <ProxyPort> Proxy server IP port, required i.e. when running\n");
1923 fprintf(stderr, " the program in a proxy server protected LAN, optional\n");
1924 fprintf(stderr, " -R <maxDelay> Reconnect mechanism with maximum delay between reconnect\n");
1925 fprintf(stderr, " attemts in seconds, default: no reconnect activated,\n");
[487]1926 fprintf(stderr, " optional\n\n");
[9403]1927 fprintf(stderr, " -M <InputMode> Sets the input mode (1 = Serial Port, 2 = IP server,\n");
[9448]1928 fprintf(stderr, " 3 = File, 4 = SISNeT Data Server, 5 = UDP server, 6 = NTRIP1 Caster,\n");
1929 fprintf(stderr, " 7 = NTRIP2 Caster in HTTP mode),\n");
[485]1930 fprintf(stderr, " mandatory\n\n");
1931 fprintf(stderr, " <InputMode> = 1 (Serial Port):\n");
[9403]1932 fprintf(stderr, " -i <Device> Serial input device, default: %s, mandatory if\n", ttyport);
[485]1933 fprintf(stderr, " <InputMode>=1\n");
[9403]1934 fprintf(stderr, " -b <BaudRate> Serial input baud rate, default: 19200 bps, mandatory\n");
[1113]1935 fprintf(stderr, " if <InputMode>=1\n");
[9403]1936 fprintf(stderr, " -f <InitFile> Name of initialization file to be send to input device,\n");
[1113]1937 fprintf(stderr, " optional\n\n");
[485]1938 fprintf(stderr, " <InputMode> = 2|5 (IP port | UDP port):\n");
[9403]1939 fprintf(stderr, " -H <ServerHost> Input host name or address, default: 127.0.0.1,\n");
[485]1940 fprintf(stderr, " mandatory if <InputMode> = 2|5\n");
[9403]1941 fprintf(stderr, " -P <ServerPort> Input port, default: 1025, mandatory if <InputMode>= 2|5\n");
1942 fprintf(stderr, " -f <ServerFile> Name of initialization file to be send to server,\n");
[485]1943 fprintf(stderr, " optional\n");
[9403]1944 fprintf(stderr, " -x <ServerUser> User ID to access incoming stream, optional\n");
1945 fprintf(stderr, " -y <ServerPass> Password, to access incoming stream, optional\n");
1946 fprintf(stderr, " -B Bind to incoming UDP stream, optional for <InputMode> = 5\n\n");
[485]1947 fprintf(stderr, " <InputMode> = 3 (File):\n");
[9403]1948 fprintf(stderr, " -s <File> File name to simulate stream by reading data from (log)\n");
1949 fprintf(stderr, " file, default is %s, mandatory for <InputMode> = 3\n\n", filepath);
[485]1950 fprintf(stderr, " <InputMode> = 4 (SISNeT Data Server):\n");
[9403]1951 fprintf(stderr, " -H <SisnetHost> SISNeT Data Server name or address,\n");
1952 fprintf(stderr, " default: 131.176.49.142, mandatory if <InputMode> = 4\n");
1953 fprintf(stderr, " -P <SisnetPort> SISNeT Data Server port, default: 7777, mandatory if\n");
[485]1954 fprintf(stderr, " <InputMode> = 4\n");
[9403]1955 fprintf(stderr, " -u <SisnetUser> SISNeT Data Server user ID, mandatory if <InputMode> = 4\n");
1956 fprintf(stderr, " -l <SisnetPass> SISNeT Data Server password, mandatory if <InputMode> = 4\n");
1957 fprintf(stderr, " -V <SisnetVers> SISNeT Data Server Version number, options are 2.1, 3.0\n");
1958 fprintf(stderr, " or 3.1, default: 3.1, mandatory if <InputMode> = 4\n\n");
1959 fprintf(stderr, " <InputMode> = 6|7 (NTRIP Version 1.0|2.0 Caster):\n");
1960 fprintf(stderr, " -H <SourceHost> Source caster name or address, default: 127.0.0.1,\n");
1961 fprintf(stderr, " mandatory if <InputMode> = 6|7\n");
1962 fprintf(stderr, " -P <SourcePort> Source caster port, default: 2101, mandatory if\n");
[9401]1963 fprintf(stderr, " <InputMode> = 6|7\n");
[9403]1964 fprintf(stderr, " -D <SourceMount> Source caster mountpoint for stream input, mandatory if\n");
1965 fprintf(stderr, " <InputMode> = 6|7\n");
1966 fprintf(stderr, " -U <SourceUser> Source caster user Id for input stream access, mandatory\n");
1967 fprintf(stderr, " for protected streams if <InputMode> = 6|7\n");
1968 fprintf(stderr, " -W <SourcePass> Source caster password for input stream access, mandatory\n");
1969 fprintf(stderr, " for protected streams if <InputMode> = 6|7\n\n");
1970 fprintf(stderr, " -O <OutputMode> Sets output mode for communication with destination caster / server\n");
[9448]1971 fprintf(stderr, " 1 = http : NTRIP Version 2.0 Caster in TCP/IP mode\n");
1972 fprintf(stderr, " 2 = rtsp : NTRIP Version 2.0 Caster in RTSP/RTP mode\n");
[487]1973 fprintf(stderr, " 3 = ntrip1: NTRIP Version 1.0 Caster\n");
[9448]1974 fprintf(stderr, " 4 = udp : NTRIP Version 2.0 Caster in Plain UDP mode\n");
1975 fprintf(stderr, " 5 = tcpip : IP server in TCP/IP mode\n\n\n");
[9403]1976 fprintf(stderr, " Defaults to NTRIP1.0, but will change to 2.0 in future versions\n");
1977 fprintf(stderr, " Note that the program automatically falls back from mode rtsp to mode http and\n");
[487]1978 fprintf(stderr, " further to mode ntrip1 if necessary.\n\n");
[9448]1979 fprintf(stderr, " -a <DestHost> Destination caster/server name or address, default: 127.0.0.1,\n");
[485]1980 fprintf(stderr, " mandatory\n");
[9448]1981 fprintf(stderr, " -p <DestPort> Destination caster/server port, default: 2101,\n");
[485]1982 fprintf(stderr, " mandatory\n");
[9403]1983 fprintf(stderr, " -m <DestMount> Destination caster mountpoint for stream upload,\n");
1984 fprintf(stderr, " only for NTRIP destination casters, mandatory\n");
1985 fprintf(stderr, " -n <DestUser> Destination caster user ID for stream upload to mountpoint,\n");
1986 fprintf(stderr, " only for NTRIP Version 2.0 destination casters, mandatory\n");
1987 fprintf(stderr, " -c <DestPass> Destination caster password for stream upload to mountpoint,\n");
1988 fprintf(stderr, " only for NTRIP destination casters, mandatory\n");
[485]1989 fprintf(stderr, " -N <STR-record> Sourcetable STR-record\n");
[9403]1990 fprintf(stderr, " optional for NTRIP Version 2.0 in RTSP/RTP and TCP/IP mode\n\n");
[485]1991 exit(rc);
1992} /* usage */
1993
1994/********************************************************************/
1995/* signal handling */
1996/********************************************************************/
1997#ifdef __GNUC__
1998static void handle_sigint(int sig __attribute__((__unused__)))
1999#else /* __GNUC__ */
2000static void handle_sigint(int sig)
2001#endif /* __GNUC__ */
[487]2002{
[9401]2003 sigint_received = 1;
[487]2004 fprintf(stderr, "WARNING: SIGINT received - ntripserver terminates\n");
2005}
[485]2006
[653]2007#ifndef WINDOWSVERSION
[487]2008#ifdef __GNUC__
2009static void handle_alarm(int sig __attribute__((__unused__)))
2010#else /* __GNUC__ */
2011static void handle_alarm(int sig)
2012#endif /* __GNUC__ */
2013{
2014 sigalarm_received = 1;
2015 fprintf(stderr, "ERROR: more than %d seconds no activity\n", ALARMTIME);
2016}
2017
2018#ifdef __GNUC__
2019static void handle_sigpipe(int sig __attribute__((__unused__)))
2020#else /* __GNUC__ */
2021static void handle_sigpipe(int sig)
2022#endif /* __GNUC__ */
[653]2023{
2024 sigpipe_received = 1;
2025}
2026#endif /* WINDOWSVERSION */
[487]2027
[9401]2028static void setup_signal_handler(int sig, void (*handler)(int)) {
[485]2029#if _POSIX_VERSION > 198800L
2030 struct sigaction action;
2031
2032 action.sa_handler = handler;
2033 sigemptyset(&(action.sa_mask));
2034 sigaddset(&(action.sa_mask), sig);
2035 action.sa_flags = 0;
2036 sigaction(sig, &action, 0);
2037#else
2038 signal(sig, handler);
2039#endif
2040 return;
2041} /* setupsignal_handler */
2042
2043/********************************************************************
2044 * base64-encoding *
[9401]2045 *******************************************************************/
2046static const char encodingTable[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
2047 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
2048 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
2049 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
2050 '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
[485]2051
2052/* does not buffer overrun, but breaks directly after an error */
2053/* returns the number of required bytes */
[9401]2054static int encode(char *buf, int size, const char *user, const char *pwd) {
[485]2055 unsigned char inbuf[3];
2056 char *out = buf;
2057 int i, sep = 0, fill = 0, bytes = 0;
2058
[9401]2059 while (*user || *pwd) {
[485]2060 i = 0;
[9401]2061 while (i < 3 && *user)
2062 inbuf[i++] = *(user++);
2063 if (i < 3 && !sep) {
2064 inbuf[i++] = ':';
2065 ++sep;
2066 }
2067 while (i < 3 && *pwd)
2068 inbuf[i++] = *(pwd++);
2069 while (i < 3) {
2070 inbuf[i++] = 0;
2071 ++fill;
2072 }
2073 if (out - buf < size - 1)
2074 *(out++) = encodingTable[(inbuf[0] & 0xFC) >> 2];
2075 if (out - buf < size - 1)
2076 *(out++) = encodingTable[((inbuf[0] & 0x03) << 4)
2077 | ((inbuf[1] & 0xF0) >> 4)];
2078 if (out - buf < size - 1) {
2079 if (fill == 2)
[485]2080 *(out++) = '=';
2081 else
[9401]2082 *(out++) = encodingTable[((inbuf[1] & 0x0F) << 2)
2083 | ((inbuf[2] & 0xC0) >> 6)];
[485]2084 }
[9401]2085 if (out - buf < size - 1) {
2086 if (fill >= 1)
[485]2087 *(out++) = '=';
2088 else
[9401]2089 *(out++) = encodingTable[inbuf[2] & 0x3F];
[485]2090 }
2091 bytes += 4;
2092 }
[9401]2093 if (out - buf < size)
[485]2094 *out = 0;
2095 return bytes;
2096}/* base64 Encoding */
2097
2098/********************************************************************
2099 * send message to caster *
[9401]2100 *********************************************************************/
2101static int send_to_caster(char *input, sockettype socket, int input_size) {
2102 int send_error = 1;
[485]2103
[9401]2104 if ((send(socket, input, (size_t) input_size, 0)) != input_size) {
2105 fprintf(stderr,
2106 "WARNING: could not send full header to Destination caster\n");
[485]2107 send_error = 0;
2108 }
2109#ifndef NDEBUG
[9401]2110 else {
[485]2111 fprintf(stderr, "\nDestination caster request:\n");
[9403]2112 fprintf(stderr, "%s\n", input);
[485]2113 }
2114#endif
2115 return send_error;
2116}/* send_to_caster */
2117
2118/********************************************************************
[487]2119 * reconnect *
[9401]2120 *********************************************************************/
2121int reconnect(int rec_sec, int rec_sec_max) {
2122 fprintf(stderr, "reconnect in <%d> seconds\n\n", rec_sec);
[487]2123 rec_sec *= 2;
[9401]2124 if (rec_sec > rec_sec_max)
2125 rec_sec = rec_sec_max;
[653]2126#ifndef WINDOWSVERSION
[487]2127 sleep(rec_sec);
[653]2128 sigpipe_received = 0;
2129#else
2130 Sleep(rec_sec*1000);
2131#endif
[487]2132 sigalarm_received = 0;
2133 return rec_sec;
2134} /* reconnect */
2135
2136/********************************************************************
[485]2137 * close session *
[9401]2138 *********************************************************************/
[1570]2139static void close_session(const char *caster_addr, const char *mountpoint,
[9401]2140 int session, char *rtsp_ext, int fallback) {
2141 int size_send_buf;
[485]2142 char send_buf[BUFSZ];
2143
[9401]2144 if (!fallback) {
2145 if ((gps_socket != INVALID_SOCKET)
2146 && ((inputmode == TCPSOCKET) || (inputmode == UDPSOCKET)
2147 || (inputmode == NTRIP1_IN) || (inputmode == NTRIP2_HTTP_IN)
2148 || (inputmode == SISNET))) {
2149 if (closesocket(gps_socket) == -1) {
[653]2150 perror("ERROR: close input device ");
2151 exit(0);
[9401]2152 } else {
[653]2153 gps_socket = -1;
[1570]2154#ifndef NDEBUG
[653]2155 fprintf(stderr, "close input device: successful\n");
2156#endif
2157 }
[9401]2158 } else if ((gps_serial != INVALID_HANDLE_VALUE) && (inputmode == SERIAL)) {
[653]2159#ifndef WINDOWSVERSION
[9401]2160 if (close(gps_serial) == INVALID_HANDLE_VALUE) {
[653]2161 perror("ERROR: close input device ");
2162 exit(0);
2163 }
2164#else
2165 if(!CloseHandle(gps_serial))
2166 {
2167 fprintf(stderr, "ERROR: close input device ");
2168 exit(0);
2169 }
2170#endif
[9401]2171 else {
[653]2172 gps_serial = INVALID_HANDLE_VALUE;
[1570]2173#ifndef NDEBUG
[653]2174 fprintf(stderr, "close input device: successful\n");
[487]2175#endif
[653]2176 }
[9401]2177 } else if ((gps_file != -1) && (inputmode == INFILE)) {
2178 if (close(gps_file) == -1) {
[653]2179 perror("ERROR: close input device ");
2180 exit(0);
[9401]2181 } else {
[653]2182 gps_file = -1;
[1570]2183#ifndef NDEBUG
[653]2184 fprintf(stderr, "close input device: successful\n");
2185#endif
2186 }
2187 }
[485]2188 }
2189
[9401]2190 if (socket_udp != INVALID_SOCKET) {
2191 if (udp_cseq > 2) {
[485]2192 size_send_buf = snprintf(send_buf, sizeof(send_buf),
[9401]2193 "TEARDOWN rtsp://%s%s/%s RTSP/1.0\r\n"
2194 "CSeq: %d\r\n"
2195 "Session: %u\r\n"
2196 "\r\n", caster_addr, rtsp_ext, mountpoint, udp_cseq++, session);
2197 if ((size_send_buf >= (int) sizeof(send_buf)) || (size_send_buf < 0)) {
[485]2198 fprintf(stderr, "ERROR: Destination caster request to long\n");
2199 exit(0);
2200 }
[9401]2201 send_to_caster(send_buf, socket_tcp, size_send_buf);
2202 strcpy(send_buf, "");
[487]2203 size_send_buf = recv(socket_tcp, send_buf, sizeof(send_buf), 0);
[485]2204 send_buf[size_send_buf] = '\0';
2205#ifndef NDEBUG
2206 fprintf(stderr, "Destination caster response:\n%s", send_buf);
2207#endif
2208 }
[9401]2209 if (closesocket(socket_udp) == -1) {
[485]2210 perror("ERROR: close udp socket");
2211 exit(0);
[9401]2212 } else {
[487]2213 socket_udp = -1;
2214#ifndef NDEBUG
2215 fprintf(stderr, "close udp socket: successful\n");
2216#endif
[485]2217 }
2218 }
2219
[9401]2220 if (socket_tcp != INVALID_SOCKET) {
2221 if (closesocket(socket_tcp) == -1) {
[487]2222 perror("ERROR: close tcp socket");
2223 exit(0);
[9401]2224 } else {
[487]2225 socket_tcp = -1;
[485]2226#ifndef NDEBUG
[487]2227 fprintf(stderr, "close tcp socket: successful\n");
2228#endif
2229 }
[485]2230 }
2231} /* close_session */
Note: See TracBrowser for help on using the repository browser.