source: ntrip/trunk/ntripclient/ntripclient.c@ 501

Last change on this file since 501 was 501, checked in by stoecker, 17 years ago

minor fixes

File size: 28.4 KB
Line 
1/*
2 Easy example NTRIP client for POSIX.
3 $Id: ntripclient.c,v 1.33 2007/10/05 15:40:24 stuerze Exp $
4 Copyright (C) 2003-2005 by Dirk Stoecker <soft@dstoecker.de>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 or read http://www.gnu.org/licenses/gpl.txt
20*/
21
22#include <ctype.h>
23#include <getopt.h>
24#include <signal.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <errno.h>
29#include <string.h>
30#include <netdb.h>
31#include <sys/types.h>
32#include <netinet/in.h>
33#include <sys/socket.h>
34#include <time.h>
35
36#ifndef COMPILEDATE
37#define COMPILEDATE " built " __DATE__
38#endif
39
40/* The string, which is send as agent in HTTP request */
41#define AGENTSTRING "NTRIP NtripClientPOSIX"
42
43#define MAXDATASIZE 1000 /* max number of bytes we can get at once */
44#define ALARMTIME (2*60)
45
46/* CVS revision and version */
47static char revisionstr[] = "$Revision: 1.33 $";
48static char datestr[] = "$Date: 2007/10/05 15:40:24 $";
49
50enum MODE { HTTP = 1, RTSP = 2, NTRIP1 = 3, AUTO = 4, END };
51
52struct Args
53{
54 const char *server;
55 const char *port;
56 const char *user;
57 const char *proxyhost;
58 const char *proxyport;
59 const char *password;
60 const char *nmea;
61 const char *data;
62 int bitrate;
63 int mode;
64};
65
66
67
68/* option parsing */
69#ifdef NO_LONG_OPTS
70#define LONG_OPT(a)
71#else
72#define LONG_OPT(a) a
73static struct option opts[] = {
74{ "bitrate", no_argument, 0, 'b'},
75{ "data", required_argument, 0, 'd'},
76{ "server", required_argument, 0, 's'},
77{ "password", required_argument, 0, 'p'},
78{ "port", required_argument, 0, 'r'},
79{ "proxyport", required_argument, 0, 'R'},
80{ "proxyhost", required_argument, 0, 'S'},
81{ "user", required_argument, 0, 'u'},
82{ "nmea", required_argument, 0, 'n'},
83{ "mode", required_argument, 0, 'M'},
84{ "help", no_argument, 0, 'h'},
85{0,0,0,0}};
86#endif
87#define ARGOPT "-d:bhp:r:s:u:n:S:R:M:"
88
89#ifdef __GNUC__
90static __attribute__ ((noreturn)) void sighandler_alarm(
91int sig __attribute__((__unused__)))
92#else /* __GNUC__ */
93static void sighandler_alarm(int sig)
94#endif /* __GNUC__ */
95{
96 fprintf(stderr, "ERROR: more than %d seconds no activity\n", ALARMTIME);
97 exit(1);
98}
99
100int stop = 0;
101#ifdef __GNUC__
102static void sighandler_int(int sig __attribute__((__unused__)))
103#else /* __GNUC__ */
104static void sighandler_alarm(int sig)
105#endif /* __GNUC__ */
106{
107 alarm(2);
108 stop = 1;
109}
110
111static const char *encodeurl(const char *req)
112{
113 char *h = "0123456789abcdef";
114 static char buf[128];
115 char *urlenc = buf;
116 char *bufend = buf + sizeof(buf) - 3;
117
118 while(*req && urlenc < bufend)
119 {
120 if(isalnum(*req)
121 || *req == '-' || *req == '_' || *req == '.')
122 *urlenc++ = *req++;
123 else
124 {
125 *urlenc++ = '%';
126 *urlenc++ = h[*req >> 4];
127 *urlenc++ = h[*req & 0x0f];
128 *req++;
129 }
130 }
131 *urlenc = 0;
132 return buf;
133}
134
135static const char *geturl(const char *url, struct Args *args)
136{
137 static char buf[1000];
138 static char *Buffer = buf;
139 static char *Bufend = buf+sizeof(buf);
140 char *h = "0123456789abcdef";
141
142 if(strncmp("ntrip:", url, 6))
143 return "URL must start with 'ntrip:'.";
144 url += 6; /* skip ntrip: */
145
146 if(*url != '@' && *url != '/')
147 {
148 /* scan for mountpoint */
149 args->data = Buffer;
150 if(*url != '?')
151 {
152 while(*url && *url != '@' && *url != ';' && *url != '/' && Buffer != Bufend)
153 *(Buffer++) = *(url++);
154 }
155 else
156 {
157 while(*url && *url != '@' && *url != '/' && Buffer != Bufend)
158 {
159 if(isalnum(*url) || *url == '-' || *url == '_' || *url == '.')
160 *Buffer++ = *url++;
161 else
162 {
163 *Buffer++ = '%';
164 *Buffer++ = h[*url >> 4];
165 *Buffer++ = h[*url & 0x0f];
166 *url++;
167 }
168 }
169 }
170 if(Buffer == args->data)
171 return "Mountpoint required.";
172 else if(Buffer >= Bufend-1)
173 return "Parsing buffer too short.";
174 *(Buffer++) = 0;
175 }
176
177 if(*url == '/') /* username and password */
178 {
179 ++url;
180 args->user = Buffer;
181 while(*url && *url != '@' && *url != ';' && *url != ':' && Buffer != Bufend)
182 *(Buffer++) = *(url++);
183 if(Buffer == args->user)
184 return "Username cannot be empty.";
185 else if(Buffer >= Bufend-1)
186 return "Parsing buffer too short.";
187 *(Buffer++) = 0;
188
189 if(*url == ':') ++url;
190
191 args->password = Buffer;
192 while(*url && *url != '@' && *url != ';' && Buffer != Bufend)
193 *(Buffer++) = *(url++);
194 if(Buffer == args->password)
195 return "Password cannot be empty.";
196 else if(Buffer >= Bufend-1)
197 return "Parsing buffer too short.";
198 *(Buffer++) = 0;
199 }
200
201 if(*url == '@') /* server */
202 {
203 ++url;
204 if(*url != '@' && *url != ':')
205 {
206 args->server = Buffer;
207 while(*url && *url != '@' && *url != ':' && *url != ';' && Buffer != Bufend)
208 *(Buffer++) = *(url++);
209 if(Buffer == args->server)
210 return "Servername cannot be empty.";
211 else if(Buffer >= Bufend-1)
212 return "Parsing buffer too short.";
213 *(Buffer++) = 0;
214 }
215
216 if(*url == ':')
217 {
218 ++url;
219 args->port = Buffer;
220 while(*url && *url != '@' && *url != ';' && Buffer != Bufend)
221 *(Buffer++) = *(url++);
222 if(Buffer == args->port)
223 return "Port cannot be empty.";
224 else if(Buffer >= Bufend-1)
225 return "Parsing buffer too short.";
226 *(Buffer++) = 0;
227 }
228
229 if(*url == '@') /* proxy */
230 {
231 ++url;
232 args->proxyhost = Buffer;
233 while(*url && *url != ':' && *url != ';' && Buffer != Bufend)
234 *(Buffer++) = *(url++);
235 if(Buffer == args->proxyhost)
236 return "Proxy servername cannot be empty.";
237 else if(Buffer >= Bufend-1)
238 return "Parsing buffer too short.";
239 *(Buffer++) = 0;
240
241 if(*url == ':')
242 {
243 ++url;
244 args->proxyport = Buffer;
245 while(*url && *url != ';' && Buffer != Bufend)
246 *(Buffer++) = *(url++);
247 if(Buffer == args->proxyport)
248 return "Proxy port cannot be empty.";
249 else if(Buffer >= Bufend-1)
250 return "Parsing buffer too short.";
251 *(Buffer++) = 0;
252 }
253 }
254 }
255 if(*url == ';') /* NMEA */
256 {
257 args->nmea = ++url;
258 while(*url)
259 ++url;
260 }
261
262 return *url ? "Garbage at end of server string." : 0;
263}
264
265static int getargs(int argc, char **argv, struct Args *args)
266{
267 int res = 1;
268 int getoptr;
269 char *a;
270 int i = 0, help = 0;
271
272 args->server = "www.euref-ip.net";
273 args->port = "2101";
274 args->user = "";
275 args->password = "";
276 args->nmea = 0;
277 args->data = 0;
278 args->bitrate = 0;
279 args->proxyhost = 0;
280 args->proxyport = "2101";
281 args->mode = AUTO;
282 help = 0;
283
284 do
285 {
286#ifdef NO_LONG_OPTS
287 switch((getoptr = getopt(argc, argv, ARGOPT)))
288#else
289 switch((getoptr = getopt_long(argc, argv, ARGOPT, opts, 0)))
290#endif
291 {
292 case 's': args->server = optarg; break;
293 case 'u': args->user = optarg; break;
294 case 'p': args->password = optarg; break;
295 case 'd':
296 if(optarg && *optarg == '?')
297 args->data = encodeurl(optarg);
298 else
299 args->data = optarg;
300 break;
301 case 'n': args->nmea = optarg; break;
302 case 'b': args->bitrate = 1; break;
303 case 'h': help=1; break;
304 case 'r': args->port = optarg; break;
305 case 'S': args->proxyhost = optarg; break;
306 case 'R': args->proxyport = optarg; break;
307 case 'M':
308 args->mode = 0;
309 if (!strcmp(optarg,"n") || !strcmp(optarg,"ntrip1"))
310 args->mode = NTRIP1;
311 else if(!strcmp(optarg,"h") || !strcmp(optarg,"http"))
312 args->mode = HTTP;
313 else if(!strcmp(optarg,"r") || !strcmp(optarg,"rtsp"))
314 args->mode = RTSP;
315 else if(!strcmp(optarg,"a") || !strcmp(optarg,"auto"))
316 args->mode = AUTO;
317 else args->mode = atoi(optarg);
318 if((args->mode == 0) || (args->mode >= END))
319 {
320 fprintf(stderr, "Mode %s unknown\n", optarg);
321 res = 0;
322 }
323 break;
324 case 1:
325 {
326 const char *err;
327 if((err = geturl(optarg, args)))
328 {
329 fprintf(stderr, "%s\n\n", err);
330 res = 0;
331 }
332 }
333 break;
334 case -1: break;
335 }
336 } while(getoptr != -1 && res);
337
338 for(a = revisionstr+11; *a && *a != ' '; ++a)
339 revisionstr[i++] = *a;
340 revisionstr[i] = 0;
341 datestr[0] = datestr[7];
342 datestr[1] = datestr[8];
343 datestr[2] = datestr[9];
344 datestr[3] = datestr[10];
345 datestr[5] = datestr[12];
346 datestr[6] = datestr[13];
347 datestr[8] = datestr[15];
348 datestr[9] = datestr[16];
349 datestr[4] = datestr[7] = '-';
350 datestr[10] = 0;
351
352 if(!res || help)
353 {
354 fprintf(stderr, "Version %s (%s) GPL" COMPILEDATE "\nUsage:\n%s -s server -u user ...\n"
355 " -d " LONG_OPT("--data ") "the requested data set\n"
356 " -s " LONG_OPT("--server ") "the server name or address\n"
357 " -p " LONG_OPT("--password ") "the login password\n"
358 " -r " LONG_OPT("--port ") "the server port number (default 2101)\n"
359 " -u " LONG_OPT("--user ") "the user name\n"
360 " -n " LONG_OPT("--nmea ") "NMEA string for sending to server\n"
361 " -b " LONG_OPT("--bitrate ") "output bitrate\n"
362 " -S " LONG_OPT("--proxyhost ") "proxy name or address\n"
363 " -R " LONG_OPT("--proxyport ") "proxy port, optional (default 2101)\n"
364 " -M " LONG_OPT("--mode ") "mode for data request\n"
365 " Valid modes are:\n"
366 " 1, h, http NTRIP Version 2.0 Caster in TCP/IP mode\n"
367 " 2, r, rtsp NTRIP Version 2.0 Caster in RTSP/RTP mode\n"
368 " 3, n, ntrip1 NTRIP Version 1.0 Caster\n"
369 " 4, a, auto automatic detection (default)\n"
370 "or using an URL:\n%s ntrip:data[/user[:password]][@[server][:port][@proxyhost[:proxyport]]][;nmea]\n"
371 , revisionstr, datestr, argv[0], argv[0]);
372 exit(1);
373 }
374 return res;
375}
376
377static const char encodingTable [64] = {
378 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
379 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
380 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
381 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
382};
383
384/* does not buffer overrun, but breaks directly after an error */
385/* returns the number of required bytes */
386static int encode(char *buf, int size, const char *user, const char *pwd)
387{
388 unsigned char inbuf[3];
389 char *out = buf;
390 int i, sep = 0, fill = 0, bytes = 0;
391
392 while(*user || *pwd)
393 {
394 i = 0;
395 while(i < 3 && *user) inbuf[i++] = *(user++);
396 if(i < 3 && !sep) {inbuf[i++] = ':'; ++sep; }
397 while(i < 3 && *pwd) inbuf[i++] = *(pwd++);
398 while(i < 3) {inbuf[i++] = 0; ++fill; }
399 if(out-buf < size-1)
400 *(out++) = encodingTable[(inbuf [0] & 0xFC) >> 2];
401 if(out-buf < size-1)
402 *(out++) = encodingTable[((inbuf [0] & 0x03) << 4)
403 | ((inbuf [1] & 0xF0) >> 4)];
404 if(out-buf < size-1)
405 {
406 if(fill == 2)
407 *(out++) = '=';
408 else
409 *(out++) = encodingTable[((inbuf [1] & 0x0F) << 2)
410 | ((inbuf [2] & 0xC0) >> 6)];
411 }
412 if(out-buf < size-1)
413 {
414 if(fill >= 1)
415 *(out++) = '=';
416 else
417 *(out++) = encodingTable[inbuf [2] & 0x3F];
418 }
419 bytes += 4;
420 }
421 if(out-buf < size)
422 *out = 0;
423 return bytes;
424}
425
426int main(int argc, char **argv)
427{
428 struct Args args;
429
430 setbuf(stdout, 0);
431 setbuf(stdin, 0);
432 setbuf(stderr, 0);
433 signal(SIGALRM,sighandler_alarm);
434 signal(SIGINT,sighandler_int);
435 alarm(ALARMTIME);
436
437 if(getargs(argc, argv, &args))
438 {
439 int sleeptime = 0;
440 do
441 {
442 int sockfd, numbytes;
443 char buf[MAXDATASIZE];
444 struct sockaddr_in their_addr; /* connector's address information */
445 struct hostent *he;
446 struct servent *se;
447 const char *server, *port, *proxyserver = 0;
448 char proxyport[6];
449 char *b;
450 long i;
451 if(sleeptime)
452 {
453 sleep(sleeptime);
454 sleeptime += 2;
455 }
456 else
457 {
458 sleeptime = 1;
459 }
460 alarm(ALARMTIME);
461 if(args.proxyhost)
462 {
463 int p;
464 if((i = strtol(args.port, &b, 10)) && (!b || !*b))
465 p = i;
466 else if(!(se = getservbyname(args.port, 0)))
467 {
468 fprintf(stderr, "Can't resolve port %s.", args.port);
469 exit(1);
470 }
471 else
472 {
473 p = ntohs(se->s_port);
474 }
475 snprintf(proxyport, sizeof(proxyport), "%d", p);
476 port = args.proxyport;
477 proxyserver = args.server;
478 server = args.proxyhost;
479 }
480 else
481 {
482 server = args.server;
483 port = args.port;
484 }
485 memset(&their_addr, 0, sizeof(struct sockaddr_in));
486 if((i = strtol(port, &b, 10)) && (!b || !*b))
487 their_addr.sin_port = htons(i);
488 else if(!(se = getservbyname(port, 0)))
489 {
490 fprintf(stderr, "Can't resolve port %s.", port);
491 exit(1);
492 }
493 else
494 {
495 their_addr.sin_port = se->s_port;
496 }
497 if(!(he=gethostbyname(server)))
498 {
499 fprintf(stderr, "Server name lookup failed for '%s'.\n", server);
500 exit(1);
501 }
502 if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
503 {
504 perror("socket");
505 exit(1);
506 }
507 their_addr.sin_family = AF_INET;
508 their_addr.sin_addr = *((struct in_addr *)he->h_addr);
509
510 if(args.data && *args.data != '%' && args.mode == RTSP)
511 {
512 struct sockaddr_in local;
513 int sockudp, localport;
514 int cseq = 1;
515 socklen_t len;
516
517 if((sockudp = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
518 {
519 perror("socket");
520 exit(1);
521 }
522 /* fill structure with local address information for UDP */
523 memset(&local, 0, sizeof(local));
524 local.sin_family = AF_INET;
525 local.sin_port = htons(0);
526 local.sin_addr.s_addr = htonl(INADDR_ANY);
527 len = sizeof(local);
528 /* bind() in order to get a random RTP client_port */
529 if((bind(sockudp, (struct sockaddr *)&local, len)) < 0)
530 {
531 perror("bind");
532 exit(1);
533 }
534 if((getsockname(sockudp, (struct sockaddr*)&local, &len)) != -1)
535 {
536 localport = ntohs(local.sin_port);
537 }
538 else
539 {
540 perror("local access failed");
541 exit(1);
542 }
543 if(connect(sockfd, (struct sockaddr *)&their_addr,
544 sizeof(struct sockaddr)) == -1)
545 {
546 perror("connect");
547 exit(1);
548 }
549 i=snprintf(buf, MAXDATASIZE-40, /* leave some space for login */
550 "SETUP rtsp://%s%s%s/%s RTSP/1.0\r\n"
551 "CSeq: %d\r\n"
552 "Ntrip-Version: Ntrip/2.0\r\n"
553 "Ntrip-Component: Ntripclient\r\n"
554 "User-Agent: %s/%s\r\n"
555 "Transport: RTP/GNSS;unicast;client_port=%u\r\n"
556 "Authorization: Basic ",
557 args.server, proxyserver ? ":" : "", proxyserver ? args.port : "",
558 args.data, cseq++, AGENTSTRING, revisionstr, localport);
559 if(i > MAXDATASIZE-40 || i < 0) /* second check for old glibc */
560 {
561 fprintf(stderr, "Requested data too long\n");
562 exit(1);
563 }
564 i += encode(buf+i, MAXDATASIZE-i-4, args.user, args.password);
565 if(i > MAXDATASIZE-4)
566 {
567 fprintf(stderr, "Username and/or password too long\n");
568 exit(1);
569 }
570 buf[i++] = '\r';
571 buf[i++] = '\n';
572 buf[i++] = '\r';
573 buf[i++] = '\n';
574 if(args.nmea)
575 {
576 int j = snprintf(buf+i, MAXDATASIZE-i, "%s\r\n", args.nmea);
577 if(j >= 0 && j < MAXDATASIZE-i)
578 i += j;
579 else
580 {
581 fprintf(stderr, "NMEA string too long\n");
582 exit(1);
583 }
584 }
585 if(send(sockfd, buf, (size_t)i, 0) != i)
586 {
587 perror("send");
588 exit(1);
589 }
590 if((numbytes=recv(sockfd, buf, MAXDATASIZE-1, 0)) != -1)
591 {
592 if(numbytes >= 17 && !strncmp(buf, "RTSP/1.0 200 OK\r\n", 17))
593 {
594 int serverport = 0, session = 0;
595 const char *portcheck = "server_port=";
596 const char *sessioncheck = "session: ";
597 int l = strlen(portcheck)-1;
598 int j=0;
599 for(i = 0; j != l && i < numbytes-l; ++i)
600 {
601 for(j = 0; j < l && tolower(buf[i+j]) == portcheck[j]; ++j)
602 ;
603 }
604 if(i == numbytes-l)
605 {
606 fprintf(stderr, "No server port number found\n");
607 exit(1);
608 }
609 else
610 {
611 i+=l;
612 while(i < numbytes && buf[i] >= '0' && buf[i] <= '9')
613 serverport = serverport * 10 + buf[i++]-'0';
614 if(buf[i] != '\r' && buf[i] != ';')
615 {
616 fprintf(stderr, "Could not extract server port\n");
617 exit(1);
618 }
619 }
620 l = strlen(sessioncheck)-1;
621 j=0;
622 for(i = 0; j != l && i < numbytes-l; ++i)
623 {
624 for(j = 0; j < l && tolower(buf[i+j]) == sessioncheck[j]; ++j)
625 ;
626 }
627 if(i == numbytes-l)
628 {
629 fprintf(stderr, "No session number found\n");
630 exit(1);
631 }
632 else
633 {
634 i+=l;
635 while(i < numbytes && buf[i] >= '0' && buf[i] <= '9')
636 session = session * 10 + buf[i++]-'0';
637 if(buf[i] != '\r')
638 {
639 fprintf(stderr, "Could not extract session number\n");
640 exit(1);
641 }
642 }
643
644 i = snprintf(buf, MAXDATASIZE,
645 "PLAY rtsp://%s%s%s/%s RTSP/1.0\r\n"
646 "CSeq: %d\r\n"
647 "Session: %d\r\n"
648 "\r\n",
649 args.server, proxyserver ? ":" : "", proxyserver ? args.port : "",
650 args.data, cseq++, session);
651
652 if(i > MAXDATASIZE || i < 0) /* second check for old glibc */
653 {
654 fprintf(stderr, "Requested data too long\n");
655 exit(1);
656 }
657 if(send(sockfd, buf, (size_t)i, 0) != i)
658 {
659 perror("send");
660 exit(1);
661 }
662 if((numbytes=recv(sockfd, buf, MAXDATASIZE-1, 0)) != -1)
663 {
664 if(numbytes >= 17 && !strncmp(buf, "RTSP/1.0 200 OK\r\n", 17))
665 {
666 struct sockaddr_in addrRTP;
667 /* fill structure with caster address information for UDP */
668 memset(&addrRTP, 0, sizeof(addrRTP));
669 addrRTP.sin_family = AF_INET;
670 addrRTP.sin_port = htons(serverport);
671 their_addr.sin_addr = *((struct in_addr *)he->h_addr);
672 len = sizeof(addrRTP);
673 int ts = 0;
674 int sn = 0;
675 int ssrc = 0;
676 int init = 0;
677 int u, v, w;
678 while(!stop && (i = recvfrom(sockudp, buf, 1526, 0,
679 (struct sockaddr*) &addrRTP, &len)) > 0)
680 {
681 alarm(ALARMTIME);
682 if(i >= 12+1 && (unsigned char)buf[0] == (2 << 6) && buf[1] == 0x60)
683 {
684 u= ((unsigned char)buf[2]<<8)+(unsigned char)buf[3];
685 v = ((unsigned char)buf[4]<<24)+((unsigned char)buf[5]<<16)
686 +((unsigned char)buf[6]<<8)+(unsigned char)buf[7];
687 w = ((unsigned char)buf[8]<<24)+((unsigned char)buf[9]<<16)
688 +((unsigned char)buf[10]<<8)+(unsigned char)buf[11];
689
690 if(init)
691 {
692 if(u < -30000 && sn > 30000) sn -= 0xFFFF;
693 if(ssrc != w || ts > v)
694 {
695 fprintf(stderr, "Illegal UDP data received.\n");
696 exit(1);
697 }
698 if(u > sn) /* don't show out-of-order packets */
699 fwrite(buf+12, (size_t)i-12, 1, stdout);
700 }
701 sn = u; ts = v; ssrc = w; init = 1;
702 }
703 else
704 {
705 fprintf(stderr, "Illegal UDP header.\n");
706 exit(1);
707 }
708 }
709 }
710 i = snprintf(buf, MAXDATASIZE,
711 "TEARDOWN rtsp://%s%s%s/%s RTSP/1.0\r\n"
712 "CSeq: %d\r\n"
713 "Session: %d\r\n"
714 "\r\n",
715 args.server, proxyserver ? ":" : "", proxyserver ? args.port : "",
716 args.data, cseq++, session);
717
718 if(i > MAXDATASIZE || i < 0) /* second check for old glibc */
719 {
720 fprintf(stderr, "Requested data too long\n");
721 exit(1);
722 }
723 if(send(sockfd, buf, (size_t)i, 0) != i)
724 {
725 perror("send");
726 exit(1);
727 }
728 }
729 else
730 {
731 fprintf(stderr, "Could not start data stream.\n");
732 exit(1);
733 }
734 }
735 else
736 {
737 fprintf(stderr, "Could not setup initial control connection.\n");
738 exit(1);
739 }
740 }
741 else
742 {
743 perror("recv");
744 exit(1);
745 }
746 }
747 else
748 {
749 if(connect(sockfd, (struct sockaddr *)&their_addr,
750 sizeof(struct sockaddr)) == -1)
751 {
752 perror("connect");
753 exit(1);
754 }
755 if(!args.data)
756 {
757 i = snprintf(buf, MAXDATASIZE,
758 "GET %s%s%s%s/ HTTP/1.0\r\n"
759 "Host: %s\r\n%s"
760 "User-Agent: %s/%s\r\n"
761 "\r\n"
762 , proxyserver ? "http://" : "", proxyserver ? proxyserver : "",
763 proxyserver ? ":" : "", proxyserver ? proxyport : "",
764 args.server, args.mode == NTRIP1 ? "" : "Ntrip-Version: Ntrip/2.0\r\n",
765 AGENTSTRING, revisionstr);
766 }
767 else
768 {
769 i=snprintf(buf, MAXDATASIZE-40, /* leave some space for login */
770 "GET %s%s%s%s/%s HTTP/1.0\r\n"
771 "Host: %s\r\n%s"
772 "User-Agent: %s/%s\r\n"
773 "Authorization: Basic "
774 , proxyserver ? "http://" : "", proxyserver ? proxyserver : "",
775 proxyserver ? ":" : "", proxyserver ? proxyport : "",
776 args.data, args.server,
777 args.mode == NTRIP1 ? "" : "Ntrip-Version: Ntrip/2.0\r\n",
778 AGENTSTRING, revisionstr);
779 if(i > MAXDATASIZE-40 || i < 0) /* second check for old glibc */
780 {
781 fprintf(stderr, "Requested data too long\n");
782 exit(1);
783 }
784 i += encode(buf+i, MAXDATASIZE-i-4, args.user, args.password);
785 if(i > MAXDATASIZE-4)
786 {
787 fprintf(stderr, "Username and/or password too long\n");
788 exit(1);
789 }
790 buf[i++] = '\r';
791 buf[i++] = '\n';
792 buf[i++] = '\r';
793 buf[i++] = '\n';
794 if(args.nmea)
795 {
796 int j = snprintf(buf+i, MAXDATASIZE-i, "%s\r\n", args.nmea);
797 if(j >= 0 && j < MAXDATASIZE-i)
798 i += j;
799 else
800 {
801 fprintf(stderr, "NMEA string too long\n");
802 exit(1);
803 }
804 }
805 }
806 if(send(sockfd, buf, (size_t)i, 0) != i)
807 {
808 perror("send");
809 exit(1);
810 }
811 if(args.data && *args.data != '%')
812 {
813 int k = 0;
814 int chunkymode = 0;
815 int starttime = time(0);
816 int lastout = starttime;
817 int totalbytes = 0;
818 int chunksize = 0;
819
820 while(!stop && (numbytes=recv(sockfd, buf, MAXDATASIZE-1, 0)) != -1)
821 {
822 alarm(ALARMTIME);
823 if(!k)
824 {
825 if(numbytes > 17 && (!strncmp(buf, "HTTP/1.1 200 OK\r\n", 17)
826 || !strncmp(buf, "HTTP/1.0 200 OK\r\n", 17)))
827 {
828 const char *datacheck = "Content-Type: gnss/data\r\n";
829 const char *chunkycheck = "Transfer-Encoding: chunked\r\n";
830 int l = strlen(datacheck)-1;
831 int j=0;
832 for(i = 0; j != l && i < numbytes-l; ++i)
833 {
834 for(j = 0; j < l && buf[i+j] == datacheck[j]; ++j)
835 ;
836 }
837 if(i == numbytes-l)
838 {
839 fprintf(stderr, "No 'Content-Type: gnss/data' found\n");
840 exit(1);
841 }
842 l = strlen(chunkycheck)-1;
843 j=0;
844 for(i = 0; j != l && i < numbytes-l; ++i)
845 {
846 for(j = 0; j < l && buf[i+j] == chunkycheck[j]; ++j)
847 ;
848 }
849 if(i < numbytes-l)
850 chunkymode = 1;
851 }
852 else if(numbytes < 12 || strncmp("ICY 200 OK\r\n", buf, 12))
853 {
854 fprintf(stderr, "Could not get the requested data: ");
855 for(k = 0; k < numbytes && buf[k] != '\n' && buf[k] != '\r'; ++k)
856 {
857 fprintf(stderr, "%c", isprint(buf[k]) ? buf[k] : '.');
858 }
859 fprintf(stderr, "\n");
860 exit(1);
861 }
862 else if(args.mode != NTRIP1)
863 {
864 fprintf(stderr, "NTRIP version 2 HTTP connection failed%s.\n",
865 args.mode == AUTO ? ", falling back to NTRIP1" : "");
866 if(args.mode == HTTP)
867 exit(1);
868 }
869 ++k;
870 }
871 else
872 {
873 sleeptime = 0;
874 if(chunkymode)
875 {
876 int stop = 0;
877 int pos = 0;
878 while(!stop && pos < numbytes)
879 {
880 switch(chunkymode)
881 {
882 case 1: /* reading number starts */
883 chunksize = 0;
884 ++chunkymode; /* no break */
885 case 2: /* during reading number */
886 i = buf[pos++];
887 if(i >= '0' && i <= '9') chunksize = chunksize*16+i-'0';
888 else if(i >= 'a' && i <= 'f') chunksize = chunksize*16+i-'a'+10;
889 else if(i >= 'A' && i <= 'F') chunksize = chunksize*16+i-'A'+10;
890 else if(i == '\r') ++chunkymode;
891 else stop = 1;
892 break;
893 case 3: /* scanning for return */
894 if(buf[pos++] == '\n') chunkymode = chunksize ? 4 : 1;
895 else stop = 1;
896 break;
897 case 4: /* output data */
898 i = numbytes-pos;
899 if(i > chunksize) i = chunksize;
900 fwrite(buf+pos, (size_t)i, 1, stdout);
901 totalbytes += i;
902 chunksize -= i;
903 pos += i;
904 if(!chunksize)
905 chunkymode = 1;
906 break;
907 }
908 }
909 if(stop)
910 {
911 fprintf(stderr, "Error in chunky transfer encoding\n");
912 break;
913 }
914 }
915 else
916 {
917 totalbytes += numbytes;
918 fwrite(buf, (size_t)numbytes, 1, stdout);
919 }
920 fflush(stdout);
921 if(totalbytes < 0) /* overflow */
922 {
923 totalbytes = 0;
924 starttime = time(0);
925 lastout = starttime;
926 }
927 if(args.bitrate)
928 {
929 int t = time(0);
930 if(t > lastout + 60)
931 {
932 lastout = t;
933 fprintf(stderr, "Bitrate is %dbyte/s (%d seconds accumulated).\n",
934 totalbytes/(t-starttime), t-starttime);
935 }
936 }
937 }
938 }
939 }
940 else
941 {
942 sleeptime = 0;
943 while(!stop && (numbytes=recv(sockfd, buf, MAXDATASIZE-1, 0)) > 0)
944 {
945 alarm(ALARMTIME);
946 fwrite(buf, (size_t)numbytes, 1, stdout);
947 }
948 }
949 close(sockfd);
950 }
951 } while(args.data && *args.data != '%' && !stop);
952 }
953 return 0;
954}
Note: See TracBrowser for help on using the repository browser.