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

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

added NTRIP V2 modes

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