1 #ifdef __FreeBSD__
2 #include <stdio.h>
3 #endif
4 #define _FILE_OFFSET_BITS 64
5 #define _GNU_SOURCE
6 #include "socket.h"
7 #include "byte.h"
8 #include "buffer.h"
9 #include "scan.h"
10 #include "ip6.h"
11 #include "str.h"
12 #include "fmt.h"
13 #include "ip4.h"
14 #include "io.h"
15 #include "case.h"
16 #include "stralloc.h"
17 #include "textcode.h"
18 #include "uint16.h"
19 #include "uint64.h"
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <signal.h>
23 #include <time.h>
24 #include <sys/time.h>
25 #include <stdlib.h>
26 #include <utime.h>
27 #ifdef __MINGW32__
28 #include <windows.h>
29 #include <fcntl.h>
30 #else
31 #include <sys/resource.h>
32 #include <sys/uio.h>
33 #endif
34 #include <sys/stat.h>
35 #include <errno.h>
36 #ifdef __MINGW32__
37 #include "windows.h"
38 #include <malloc.h>
39 #else
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <netinet/tcp.h>
43 #include <netdb.h>
44 #include "havealloca.h"
45 #endif
46 #include <assert.h>
47 #include <ctype.h>
48 #include <string.h>
49
50 size_t sent;
51
52 #ifndef __linux__
strndup(const char * s,size_t n)53 char *strndup(const char *s,size_t n) {
54 char *tmp=!(n+1)?0:(char *)malloc(n+1);
55 if (!tmp) return 0;
56 strncpy(tmp,s,n);
57 tmp[n]=0;
58 return tmp;
59 }
60 #endif
61
62 int dostats;
63 int dosync;
64 time_t ims=0;
65 int verbose=0;
66 int ignoreeof;
67
68 char* todel;
69
alarm_handler(int dummy)70 void alarm_handler(int dummy) {
71 (void)dummy;
72 if (todel) unlink(todel);
73 exit(1);
74 }
75
76 static void clearstats();
77
carp(const char * routine)78 static void carp(const char* routine) {
79 clearstats();
80 buffer_puts(buffer_2,"dl: ");
81 buffer_puts(buffer_2,routine);
82 if (routine[0] && routine[str_len(routine)-1]!='\n') {
83 buffer_puts(buffer_2,": ");
84 buffer_puterror(buffer_2);
85 buffer_putnlflush(buffer_2);
86 } else
87 buffer_flush(buffer_2);
88 }
89
panic(const char * routine)90 static void panic(const char* routine) {
91 carp(routine);
92 exit(111);
93 }
94
95 static unsigned long long int total;
96 static unsigned long long resumeofs;
97
98 static int statsprinted;
99
printstats(unsigned long long nextchunk,int fd)100 void printstats(unsigned long long nextchunk,int fd) {
101 static unsigned long long int finished;
102 static struct timeval start,now,prev;
103 finished+=nextchunk;
104 if (start.tv_sec==0) {
105 gettimeofday(&start,0); now=start; prev=start;
106 return;
107 }
108 prev=now; gettimeofday(&now,0);
109 if (prev.tv_sec!=now.tv_sec) {
110 char received[FMT_ULONG], totalsize[FMT_ULONG], timedone[FMT_ULONG], percent[10];
111 char speed[FMT_ULONG+20];
112 size_t i,j;
113 #ifndef __MINGW32__
114 if (dosync) fsync(fd);
115 #endif
116 if (!dostats) return;
117 if (total) {
118 if (total>1000000000)
119 i=finished/(total/10000);
120 else
121 i=finished*10000/total;
122 j=fmt_ulong(percent,i/100);
123 percent[j]='.';
124 percent[j+1]=((i/10)%10)+'0';
125 percent[j+2]=(i%10)+'0';
126 percent[j+3]=0;
127 } else
128 strcpy(percent,"100.00");
129 j=fmt_humank(received,resumeofs+finished);
130 if (received[j-1]<='9') received[j++]='i';
131 received[j]=0;
132 j=fmt_humank(totalsize,resumeofs+total);
133 if (totalsize[j-1]<='9') totalsize[j++]='i';
134 totalsize[j]=0;
135
136 if (now.tv_sec-start.tv_sec>=60) {
137 j=fmt_ulong(timedone,(now.tv_sec-start.tv_sec)/60);
138 timedone[j]=':';
139 i=(now.tv_sec-start.tv_sec)%60;
140 timedone[j+1]=(i/10)+'0';
141 timedone[j+2]=(i%10)+'0';
142 timedone[j+3]=0;
143 } else {
144 j=fmt_ulong(timedone,now.tv_sec-start.tv_sec);
145 j+=fmt_str(timedone+j," sec");
146 timedone[j]=0;
147 }
148
149 if (now.tv_sec-start.tv_sec>1 && total) {
150 unsigned long timediff=(now.tv_sec-start.tv_sec)*100;
151 timediff += (now.tv_usec-start.tv_usec)/10000;
152
153 i=finished*100/timediff;
154 j=fmt_str(speed," (");
155 j+=fmt_humank(speed+j,i);
156 j+=fmt_str(speed+j,"iB/sec)"+(i>1000));
157 speed[j]=0;
158 } else
159 speed[0]=0;
160
161 if (now.tv_sec > start.tv_sec+3 && now.tv_sec-start.tv_sec) {
162 unsigned long long int bps=finished/(now.tv_sec-start.tv_sec);
163 size_t k=(total-finished)/bps;
164 char lm[FMT_ULONG];
165
166 if (k>=60) {
167 j=fmt_ulong(lm,k/60);
168 lm[j]=':';
169 i=k%60;
170 lm[j+1]=(i/10)+'0';
171 lm[j+2]=(i%10)+'0';
172 lm[j+3]=0;
173 } else {
174 j=fmt_ulong(lm,k);
175 j+=fmt_str(lm+j," sec");
176 lm[j]=0;
177 }
178
179 buffer_putm(buffer_2,"\r",percent,"% done; got ",received,"B ");
180 if (total)
181 buffer_putm(buffer_2,"of ",totalsize,"B ");
182 buffer_putmflush(buffer_2,"in ",timedone,speed,", ",lm," to go. ");
183 } else {
184 buffer_putm(buffer_2,"\r",percent,"% done; got ",received,"B ");
185 if (total)
186 buffer_putm(buffer_2,"of ",totalsize,"B ");
187 buffer_putmflush(buffer_2,"in ",timedone,speed,". ");
188 }
189 statsprinted=1;
190 }
191 }
192
clearstats()193 static void clearstats() {
194 if (statsprinted) buffer_putsflush(buffer_2,"\r\e[K");
195 }
196
197
make_connection(char * ip,uint16 port,uint32 scope_id,const char * buf,size_t * len)198 static int make_connection(char* ip,uint16 port,uint32 scope_id,const char* buf,size_t* len) {
199 int v6=byte_diff(ip,12,V4mappedprefix);
200 int s;
201 ssize_t r;
202 if (v6) {
203 s=socket_tcp6b();
204 #ifdef TCP_NODELAY
205 setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(int[]){ 1 },sizeof(int));
206 #endif
207 if ((r=socket_fastopen_connect6(s,ip,port,scope_id,buf,*len))==-1) {
208 #if 0
209 char a[100],b[100],c[100];
210 a[fmt_ulong(a,port)]=0;
211 b[fmt_ulong(b,scope_id)]=0;
212 c[fmt_ip6c(c,ip)]=0;
213 printf("socket_connect6(%s,%s,%s) failed!\n",c,a,b);
214 #endif
215 carp("socket_fastopen_connect6");
216 close(s);
217 return -1;
218 }
219 } else {
220 s=socket_tcp4b();
221 #ifdef TCP_NODELAY
222 setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(int[]){ 1 },sizeof(int));
223 #endif
224 if ((r=socket_fastopen_connect4(s,ip+12,port,buf,*len))==-1) {
225 carp("socket_connect4");
226 close(s);
227 return -1;
228 }
229 }
230 *len=r;
231 return s;
232 }
233
234 struct cookie {
235 const char* domain, * path, * name, * value;
236 struct cookie* next;
237 }* cookies;
238
addcookie(const char * s,const char * curdomain)239 void addcookie(const char* s,const char* curdomain) {
240 struct cookie* n;
241 char* x;
242 char* t=strchr(s,'\n');
243 if (!t) t=strchr(s,0);
244 if (t>s && t[-1]=='\r') --t;
245 if (case_starts(s,"set-cookie:")) {
246 s += sizeof("set-cookie");
247 while (*s==' ' || *s=='\t') ++s;
248 }
249 if (s==t) return;
250 if (!(x=strndup(s,t-s))) return;
251 if (!(n=malloc(sizeof(*n)))) goto kaputt2;
252 n->name=x;
253 while (*x && *x!='=') ++x;
254 if (*x!='=') {
255 kaputt:
256 free(n);
257 kaputt2:
258 free(x);
259 return;
260 }
261 *x=0;
262 n->value=++x;
263 while (*x && *x!=';' && *x!=' ' && *x!='\t') ++x;
264 n->domain=curdomain; n->path="/";
265 if (*x) {
266 if (*x!=';') goto kaputt;
267 *x=0;
268 ++x;
269 while (*x) {
270 char next;
271 while (*x==' ' || *x=='\t') ++x;
272 if (!(t=strchr(x,';'))) t=strchr(x,0);
273 next=*t; *t=0;
274 if (case_starts(x,"path=")) {
275 x+=sizeof("path");
276 n->path=x;
277 } else if (case_starts(x,"domain=")) {
278 x+=sizeof("domain");
279 n->domain=x;
280 }
281 if (!next) break;
282 x=t+1;
283 }
284 }
285 /* check if the domain is valid */
286 if (n->domain != curdomain) {
287 size_t i,a;
288 /* n->domain must be a suffic of curdomain or the other way around */
289 i=strlen(n->domain);
290 a=strlen(curdomain);
291 if (i<=a && case_diffs(n->domain,curdomain+a-i)) goto kaputt;
292 if (a<i && case_diffs(n->domain+i-a,curdomain)) goto kaputt;
293 /* can't set cookie for TLD */
294 for (i=a=0; n->domain[i]; ++i)
295 if (n->domain[i]=='.') ++a;
296 if (a<2) goto kaputt;
297 /* here we would have to check for cases like ".co.uk", but since
298 * this is just a trivial downloader without persistent cookies,
299 * I'll pass, knowingly breaking ".x.org" */
300 if (strlen(n->domain)<sizeof(".co.uk")) goto kaputt;
301 }
302 // printf("Cookie: \"%s\" = \"%s\", domain=\"%s\", path=\"%s\"\n",n->name,n->value,n->domain,n->path);
303 /* now see if the same cookie is already there */
304 {
305 struct cookie** c;
306 for (c=&cookies; *c; c=&((*c)->next)) {
307 if (!strcmp((*c)->name,n->name) && !strcmp((*c)->domain,n->domain)) {
308 (*c)->name=n->name;
309 (*c)->value=n->value;
310 (*c)->path=n->path;
311 (*c)->domain=n->domain;
312 free(n);
313 return;
314 }
315 }
316 *c=n;
317 n->next=0;
318 }
319 }
320
fmt_cookies(char * dest,const char * domain,const char * path)321 size_t fmt_cookies(char* dest,const char* domain,const char* path) {
322 struct cookie* c;
323 size_t sum=0;
324 size_t l=strlen(domain);
325 sum+=fmt_str(dest,"Cookie: ");
326 for (c=cookies; c; c=c->next) {
327 size_t k=strlen(c->domain);
328 if (l<k) continue;
329 if (case_equals(domain+l-k,c->domain) && case_starts(path,c->path))
330 sum+=fmt_strm(dest?dest+sum:0,c->name,"=",c->value,"; ");
331 }
332 if (sum>8) {
333 if (dest) {
334 dest[sum-2]='\r';
335 dest[sum-1]='\n';
336 }
337 return sum;
338 } else
339 return 0;
340 }
341
342
343 struct utimbuf u;
344
345 char* location;
346
readanswer(int s,const char * filename,const char * curdomain,int onlyprintlocation,uint16_t port)347 static int readanswer(int s,const char* filename,const char* curdomain,int onlyprintlocation,uint16_t port) {
348 char buf[8192];
349 int i,j,body=-1,r;
350 int64 d;
351 unsigned long httpcode;
352 unsigned long long rest;
353 int nocl;
354 i=0; d=-1; httpcode=0; todel=(char*)filename;
355 while ((r=read(s,buf+i,sizeof(buf)-i)) > 0) {
356 i+=r;
357 for (j=0; j+3<i; ++j) {
358 if (buf[j]=='\r' && buf[j+1]=='\n' && buf[j+2]=='\r' && buf[j+3]=='\n') {
359 unsigned long code;
360 body=j+4;
361 if (scan_ulong(buf+9,&code))
362 httpcode=code;
363 else
364 goto kaputt;
365 if (onlyprintlocation && (code/10 != 30)) return 0;
366 if (ims) {
367 /* some crappy web servers (*cough* dl.google.com *cough*) do
368 * not support If-Modified-Since, so do checking ourselves */
369 size_t i;
370 for (i=0; i+sizeof("Last-Modified: Fri, 22 Jan 2010 21:00:00")<j; ++i) {
371 if (case_starts(buf+i,"Last-Modified:")) {
372 i+=sizeof("Last-Modified:");
373 while (i<j && (buf[i]==' ' || buf[i]=='\t')) ++i;
374 if (buf[i+scan_httpdate(buf+i,&u.actime)]=='\r') {
375 if (u.actime<=ims) {
376 if (verbose)
377 buffer_putmflush(buffer_2,"File not modified (but server ignores If-Modified-Since), aborting download...\n");
378 close(d);
379 return 0;
380 }
381 }
382 }
383 }
384 }
385 if ((resumeofs && code==206 && io_appendfile(&d,filename)==0) ||
386 (!resumeofs && code==200 && ((strcmp(filename,""))?io_createfile(&d,filename)==0:((d=1)-1))))
387 panic("creat");
388 if (d==-1) {
389 if (httpcode==301 || httpcode==302 || httpcode==303) {
390 char* l=buf;
391 buf[r]=0;
392 /* extract cookies */
393 while ((l=strchr(l,'\n'))) {
394 ++l;
395 if (case_starts(l,"set-cookie:"))
396 addcookie(l,curdomain);
397 }
398 /* extract and go to location */
399 if ((l=strstr(buf,"\nLocation:"))) {
400 l+=10;
401 while (*l == ' ' || *l == '\t') ++l;
402 location=l;
403 while (*l && *l != '\r' && *l != '\n') ++l;
404 *l=0;
405 if (*location=='/') {
406 char portbuf[FMT_ULONG];
407 l=location;
408 /* *sigh* relative redirect, take parts from old url */
409 location=malloc(l-location+strlen(curdomain)+100);
410 if (location) {
411 portbuf[fmt_ulong(portbuf,port)]=0;
412 location[fmt_strm(location,"http://",curdomain,":",portbuf,l)]=0;
413 }
414 } else
415 location=strndup(location,l-location);
416 return -2;
417 }
418 return -1;
419 }
420 for (j=0; buf[j]!='\n'; ++j) ;
421 write(2,buf,j+1);
422 return 0;
423 }
424 if (i-j-4)
425 if (write(d,buf+body,i-j-4)!=i-j-4) panic("write");
426 break;
427 }
428 }
429 if (body!=-1) {
430 if (byte_diff(buf,7,"HTTP/1.")) {
431 kaputt:
432 buffer_putsflush(buffer_2,"invalid HTTP response!\n");
433 return -1;
434 }
435 break;
436 }
437 }
438 if (r==-1) return -1;
439 if (d==1) dostats=!isatty(1);
440 if (httpcode!= (resumeofs?206:200)) return 0;
441 rest=-1; nocl=1;
442 buf[r]=0;
443 for (j=0; j<r; j+=str_chr(buf+j,'\n')) {
444 if (j+17<r && case_equalb(buf+j,17,"\nContent-Length: ")) {
445 char* c=buf+j+17;
446 if (c[scan_ulonglong(c,&rest)]!='\r') {
447 buffer_putsflush(buffer_2,"invalid Content-Length header!\n");
448 return -1;
449 }
450 nocl=0;
451 } else if (j+16<r && case_equalb(buf+j,16,"\nLast-Modified: ")) {
452 char* c=buf+j+16;
453 if (c[scan_httpdate(c,&u.actime)]!='\r') {
454 buffer_putsflush(buffer_2,"invalid Last-Modified header!\n");
455 return -1;
456 }
457 }
458 ++j;
459 }
460 total=rest;
461 rest-=(r-body);
462 printstats(total-rest,d);
463 while (nocl || rest) {
464 r=read(s,buf,nocl?sizeof(buf):(rest>sizeof(buf)?sizeof(buf):rest));
465 if (r<1) {
466 if (r==-1)
467 carp("read from HTTP socket");
468 else {
469 if (ignoreeof) nocl=1;
470 if (nocl) break;
471 buffer_puts(buffer_2,"early HTTP EOF; expected ");
472 buffer_putulong(buffer_2,rest);
473 buffer_putsflush(buffer_2," more bytes!\n");
474 return -1;
475 }
476 } else {
477 printstats(r,d);
478 if (write(d,buf,r)!=r)
479 panic("write");
480 rest-=r;
481 }
482 }
483 close(d);
484 chmod(filename,0644);
485 return 0;
486 }
487
488 static stralloc ftpresponse;
489
readftpresponse(buffer * b)490 static int readftpresponse(buffer* b) {
491 char c;
492 int i,res,cont=0,num;
493 if (!stralloc_copys(&ftpresponse,"")) panic("malloc");
494 for (i=res=0; i<3; ++i) {
495 if (buffer_getc(b,&c)!=1) panic("ftp command response read error");
496 if (c<'0' || c>'9') panic("invalid ftp command response\n");
497 res=res*10+c-'0';
498 }
499 num=3;
500 for (i=3; ; ++i) {
501 if (buffer_getc(b,&c)!=1) panic("ftp command response read error");
502 if (!stralloc_append(&ftpresponse,&c)) panic("malloc");
503 if (i==0) {
504 cont=0; num=0;
505 if (c==' ' || c=='\t') cont=1;
506 }
507 if (i<3 && c>='0' && c<='9') ++num;
508 if (i==3 && num==3) cont=(c=='-');
509 if (c=='\n') {
510 if (cont) i=-1; else break;
511 }
512 }
513 return res;
514 }
515
ftpcmd(int s,buffer * b,const char * cmd)516 static int ftpcmd(int s,buffer* b,const char* cmd) {
517 int l=str_len(cmd);
518 if (write(s,cmd,l)!=l) panic("ftp command write error");
519 return readftpresponse(b);
520 }
521
ftpcmd2(int s,buffer * b,const char * cmd,const char * param)522 static int ftpcmd2(int s,buffer* b,const char* cmd,const char* param) {
523 int l=str_len(cmd);
524 int l2=str_len(param);
525 #ifdef __MINGW32__
526 char* buf=alloca(l+l2+3);
527 memcpy(buf,cmd,l);
528 memcpy(buf+l,param,l2);
529 memcpy(buf+l+l2,"\r\n",2);
530 if (write(s,buf,l+l2+2)!=l+l2+2) panic("ftp command write error");
531 #else
532 struct iovec v[3];
533 v[0].iov_base=(char*)cmd; v[0].iov_len=l;
534 v[1].iov_base=(char*)param; v[1].iov_len=l2;
535 v[2].iov_base="\r\n"; v[2].iov_len=2;
536 if (writev(s,v,3)!=l+l2+2) panic("ftp command write error");
537 #endif
538 return readftpresponse(b);
539 }
540
scan_int2digit(const char * s,int * i)541 static int scan_int2digit(const char* s, int* i) {
542 if (s[0]<'0' || s[0]>'9' || s[1]<'0' || s[1]>'9') return 0;
543 *i=(s[0]-'0')*10 + s[1]-'0';
544 return 2;
545 }
546
issafe(unsigned char c)547 static inline int issafe(unsigned char c) {
548 return (c!='"' && c>' ' && c!='+');
549 }
550
fmt_urlencoded(char * dest,const char * src,size_t len)551 size_t fmt_urlencoded(char* dest,const char* src,size_t len) {
552 register const unsigned char* s=(const unsigned char*) src;
553 unsigned long written=0,i;
554 for (i=0; i<len; ++i) {
555 if (!issafe(s[i])) {
556 if (dest) {
557 dest[written]='%';
558 dest[written+1]=fmt_tohex(s[i]>>4);
559 dest[written+2]=fmt_tohex(s[i]&15);
560 }
561 written+=3;
562 } else {
563 if (dest) dest[written]=s[i]; ++written;
564 }
565 }
566 return written;
567 }
568
validatesmb(char * buf,size_t wanted,unsigned char type,unsigned char wordcount,unsigned short bytecount,unsigned short tid,unsigned short mid)569 static int validatesmb(char* buf,size_t wanted,unsigned char type,unsigned char wordcount,
570 unsigned short bytecount,unsigned short tid,unsigned short mid) {
571 if (wanted<wordcount*2+0x23+bytecount) return -1; // too short?
572 if (!byte_equal(buf,4,"\xffSMB")) return -1; // SMB magic?
573 if ((unsigned char)buf[4]!=type) return -1; // wrong message type?
574 if (uint16_read(buf+12)!=0) return -1; // process id high == 0?
575 if (uint16_read(buf+24)!=tid) return -1; // right tree id?
576 if (uint16_read(buf+26)!=23) return -1; // right process id?
577 if (uint16_read(buf+30)!=mid) return -1; // right multiplex id?
578 if (buf[0x20]<wordcount) return -1;
579 if (uint16_read(buf+0x20+wordcount*2)<bytecount) return -1;
580 if (wanted<wordcount*2+0x22+uint16_read(buf+0x21+wordcount*2)) return -1; // too short
581 return 0;
582 }
583
readnetbios(buffer * b,char * buf,size_t * wanted)584 static void readnetbios(buffer* b,char* buf,size_t* wanted) {
585 if (buffer_get(b,buf,4)!=4) panic("short read\n");
586 *wanted=(unsigned char)buf[1] * 65535 +
587 (unsigned char)buf[2] * 256 +
588 (unsigned char)buf[3];
589 }
590
negotiatesocksconnection(int sock,const char * host,unsigned short port,char * sockname,unsigned short * socknameport)591 static int negotiatesocksconnection(int sock,const char* host,unsigned short port,char* sockname,unsigned short* socknameport) {
592 char buf[300];
593 size_t hl=strlen(host);
594 if (verbose)
595 buffer_putsflush(buffer_1,"SOCKS handshake... ");
596 /* version 5, 1 auth method, auth method none */
597 // if (write(sock,"\x05\x01\x00",3)!=3 ||
598 if (sent!=3 ||
599 read(sock,buf,2)!=2 ||
600 buf[0]!=5 || buf[1]!=0) {
601 buffer_putsflush(buffer_2,"dl: SOCKS handshake failed\n");
602 return -1;
603 }
604 if (verbose)
605 buffer_putsflush(buffer_1,"SOCKS connect... ");
606 /* version 5, command: connect (1), reserved (0), address type: domain name (3) */
607 memcpy(buf,"\x05\x01\x00\x03",4);
608 if (hl>255) {
609 buffer_putsflush(buffer_2,"dl: host name too long (SOCKS only supports up to 255)\n");
610 return -1;
611 }
612 buf[4]=hl;
613 memcpy(buf+5,host,hl);
614 uint16_pack_big(buf+5+hl,port);
615 if (write(sock,buf,5+hl+2)!=5+hl+2 ||
616 read(sock,buf,4)!=4 ||
617 buf[0]!=5 || buf[2]!=0) {
618 kaputt:
619 buffer_putsflush(buffer_2,"dl: received invalid reply to SOCKS connect request\n");
620 return -1;
621 }
622 switch (buf[1]) {
623 case 0: errno=0; break;
624 case 2: errno=EACCES; break;
625 case 3: errno=ENETUNREACH; break;
626 case 4: errno=EHOSTUNREACH; break;
627 case 5: errno=ECONNREFUSED; break;
628 case 6: errno=ETIMEDOUT; break;
629 default: errno=EINVAL;
630 }
631 if (errno) panic("SOCKS connect");
632 {
633 size_t r;
634 switch (buf[3]) {
635 case 1: r=6; break;
636 case 4: r=18; break;
637 default:
638 goto kaputt;
639 }
640 if (read(sock,buf+4,r)!=r) goto kaputt;
641 if (verbose) {
642 if (buf[3]==1) {
643 if (sockname) {
644 memcpy(sockname,V4mappedprefix,12);
645 memcpy(sockname+12,buf+4,4);
646 }
647 if (socknameport) *socknameport=uint16_read_big(buf+4+4);
648 buf[100+fmt_ip4(buf+100,buf+4)]=0;
649 buf[200+fmt_ulong(buf+200,uint16_read_big(buf+4+4))]=0;
650 } else if (buf[3]==4) {
651 if (sockname) memcpy(sockname,buf+4,16);
652 if (socknameport) *socknameport=uint16_read_big(buf+4+16);
653 buf[100+fmt_ip6(buf+100,buf+4)]=0;
654 buf[200+fmt_ulong(buf+200,uint16_read_big(buf+4+16))]=0;
655 }
656 buffer_putmflush(buffer_1,"success! Bound to ",buf+100," port ",buf+200,".\n");
657 }
658 }
659 return 0;
660 }
661
fmt_num2(char * dest,int i)662 static void fmt_num2(char *dest,int i) {
663 dest[0]=i/10+'0';
664 dest[1]=i%10+'0';
665 }
666
main(int argc,char * argv[])667 int main(int argc,char* argv[]) {
668 int useport=0;
669 int usev4=0;
670 int newer=0;
671 int resume=0;
672 int keepalive=0;
673 int imode=0;
674 int longlist=0;
675 int onlyprintlocation=0;
676 char ip[16];
677 uint16 port=80, proxyport=0, connport=0, socksport=1080;
678 uint32 scope_id=0;
679 stralloc ips={0};
680 int s;
681 char* request=0;
682 int rlen=0;
683 char* filename=0;
684 char* pathname=0;
685 char* output=0;
686 char* useragent="dl/1.0";
687 char* referer=0;
688 enum {HTTP, FTP, SMB} mode;
689 int skip;
690 buffer ftpbuf;
691 char* host,* proxyhost=0,* connhost=0;
692 char* socksproxyhost=0;
693 char externalsocksip[16];
694 unsigned short externalsocksport;
695
696 #if 0
697 addcookie("Set-cookie: RMID=0478b6d1254f4816a29724b0; expires=Wednesday, 29-Apr-2009 04:22:47 GMT; path=/; domain=.nytimes.com\r\n","www.nytimes.com");
698 addcookie("Set-cookie: NYT_GR=4816a747-xr4Bk90ylLV96+EIoKqc+A; path=/; domain=.nytimes.com","www.nytimes.com");
699 addcookie("Set-cookie: NYT-S=0MOZ7vC0h8ZsDDXrmvxADeHCpDcwlNkC5FdeFz9JchiAI6GpR90PNu0YV.Ynx4rkFI; path=/; domain=.nytimes.com","www.nytimes.com");
700 {
701 char buf[1024];
702 size_t l;
703 l=fmt_cookies(0,"www.nytimes.com","/2008/04/29/washington/29scotus.html?partner=rssnyt&emc=rss");
704 printf("l=%zu\n",l);
705 if (l<1024) {
706 buf[fmt_cookies(buf,"www.nytimes.com","/2008/04/29/washington/29scotus.html?partner=rssnyt&emc=rss")]=0;
707 printf("buf=\"%s\" (%zu)\n",buf,strlen(buf));
708 }
709 }
710 #endif
711
712 dostats=isatty(2);
713
714 #ifndef __MINGW32__
715 signal(SIGPIPE,SIG_IGN);
716 #endif
717
718 for (;;) {
719 int c=getopt(argc,argv,"i:ko4nvra:O:U:R:lsLI");
720 if (c==-1) break;
721 switch (c) {
722 case 'k':
723 keepalive=1;
724 break;
725 case 'I':
726 ignoreeof=1;
727 break;
728 case 'n':
729 newer=1;
730 break;
731 case 'i':
732 {
733 struct stat ss;
734 if (stat(optarg,&ss)==0) {
735 ims=ss.st_mtime;
736 imode=1;
737 }
738 }
739 break;
740 case 'r':
741 resume=1;
742 break;
743 case 'o':
744 useport=1;
745 break;
746 case '4':
747 usev4=1;
748 break;
749 case 'v':
750 verbose=1;
751 break;
752 case 'O':
753 output=optarg;
754 break;
755 case 'U':
756 useragent=optarg;
757 break;
758 case 'R':
759 referer=optarg;
760 break;
761 case 'l':
762 onlyprintlocation=1;
763 break;
764 case 's':
765 dosync=1;
766 break;
767 case 'L':
768 longlist=1;
769 break;
770 case 'a':
771 #ifndef __MINGW32__
772 {
773 unsigned long n;
774 signal(SIGALRM,alarm_handler);
775 if (optarg[scan_ulong(optarg,&n)]==0)
776 alarm(n);
777 break;
778 }
779 #endif
780 case '?':
781 usage:
782 buffer_putsflush(buffer_2,"usage: dl [-i file] [-no4v] url\n"
783 " -i fn only fetch file if it is newer than fn\n"
784 " -n only fetch file if it is newer than local copy\n"
785 " -r resume\n"
786 " -4 use PORT and PASV instead of EPRT and EPSV, only connect using IPv4\n"
787 " -o use PORT and EPRT instead of PASV and EPSV\n"
788 " -a n abort after n seconds\n"
789 " -O fn write output to fn\n"
790 " -U s set User-Agent HTTP header\n"
791 " -R s set Referer HTTP header\n"
792 " -l just print value of Location: header\n"
793 " -L long ftp directory listing, not just names\n"
794 " -s sync after local write\n"
795 " -I do not treat early HTTP EOF as error\n"
796 " -v be verbose\n");
797 return 0;
798 }
799 }
800 #ifdef __MINGW32__
801 _fmode=O_BINARY;
802 #endif
803
804 if (!argv[optind]) goto usage;
805 again:
806 {
807 static int redirects=0;
808 if (++redirects>5) panic("too many redirects!\n");
809 }
810
811 { // unescape url
812 size_t i,j;
813 i=scan_urlencoded2(argv[optind],argv[optind],&j);
814 if (argv[optind][i]) panic("invalid urlencoding in url!\n");
815 argv[optind][j]=0;
816 }
817
818 mode=HTTP;
819 if (byte_diff(argv[optind],skip=7,"http://")) {
820 if (byte_diff(argv[optind],skip=6,"ftp://")) {
821 if (byte_diff(argv[optind],skip=6,"smb://")) goto usage;
822 mode=SMB;
823 port=445;
824 } else {
825 mode=FTP;
826 proxyhost=getenv("ftp_proxy");
827 port=21;
828 }
829 } else
830 proxyhost=getenv("http_proxy");
831 socksproxyhost=getenv("SOCKS5_SERVER");
832 if (!socksproxyhost) socksproxyhost=getenv("SOCKS_SERVER");
833 if (socksproxyhost) {
834 char* c=strchr(socksproxyhost,':');
835 if (c) {
836 *c=0;
837 if (c[1+scan_ushort(c+1,&socksport)]) {
838 buffer_putsflush(buffer_2,"invalid socks proxy environment syntax\n");
839 return 1;
840 }
841 }
842 }
843
844 /* do we have a proxy? */
845 if (proxyhost && !proxyport) {
846 size_t i;
847 /* expect format "http://localhost:3128" */
848 if (byte_equal(proxyhost,7,"http://")) proxyhost+=7;
849 i=str_chr(proxyhost,'/');
850 if (proxyhost[i]=='/') proxyhost[i]=0;
851 i=str_rchr(proxyhost,':');
852 if (proxyhost[i]!=':' ||
853 proxyhost[i+1+scan_ushort(proxyhost+i+1,&proxyport)]) {
854 buffer_putsflush(buffer_2,"invalid proxy environment syntax\n");
855 return 1;
856 }
857 proxyhost[i]=0;
858 connhost=proxyhost;
859 connport=proxyport;
860 mode=HTTP;
861 }
862
863 {
864 int colon;
865 int slash;
866 char* c;
867 host=argv[optind]+skip;
868 colon=str_chr(host,':');
869 slash=str_chr(host,'/');
870 if (host[0]=='[') { /* ipv6 IP notation */
871 int tmp;
872 ++host;
873 --colon; --slash;
874 tmp=str_chr(host,']');
875 if (host[tmp]==']') host[tmp]=0;
876 if (host[tmp+1]==':') colon=tmp+1;
877 if (colon<tmp+1) colon=tmp+1+str_len(host+tmp+1);
878 }
879 if (colon<slash) {
880 host[colon]=0;
881 c=host+colon+1;
882 if (c[scan_ushort(c,&port)]!='/') goto usage;
883 *c=0;
884 }
885 // host[colon]=0;
886 c=host+slash;
887 pathname=c;
888 *c=0;
889 {
890 char* tmp=alloca(str_len(host)+1);
891 tmp[fmt_str(tmp,host)]=0;
892 host=tmp;
893 }
894 *c='/';
895 {
896 int tmp=str_chr(host,'%');
897 if (host[tmp]) {
898 host[tmp]=0;
899 scope_id=socket_getifidx(host+tmp+1);
900 if (scope_id==0) {
901 buffer_puts(buffer_2,"dl: warning: network interface ");
902 buffer_puts(buffer_2,host+tmp+1);
903 buffer_putsflush(buffer_2," not found.\n");
904 }
905 }
906 }
907
908 if (!proxyhost) {
909 connhost=host;
910 connport=port;
911 }
912
913 {
914 struct addrinfo hints, *ai, *aitop;
915 int gaierr;
916 char p[FMT_ULONG];
917 const char* tolookup=socksproxyhost?socksproxyhost:connhost;
918 p[fmt_ulong(p,connport)]=0;
919 memset(&hints,0,sizeof(hints));
920 hints.ai_family=AF_UNSPEC;
921 hints.ai_flags=0;
922 hints.ai_socktype=0;
923
924 ips.len=0;
925 if ((gaierr=scan_ip6if(tolookup,ip,&scope_id)) && tolookup[gaierr]==0) {
926 /* ip given, no dns needed */
927 stralloc_catb(&ips,ip,16);
928 stralloc_catb(&ips,(char*)&scope_id,4);
929 goto nodns;
930 }
931 if (verbose) buffer_putsflush(buffer_1,"DNS lookup... ");
932 if ((gaierr = getaddrinfo(tolookup,p,&hints,&aitop)) != 0 || !aitop) {
933 buffer_puts(buffer_2,"dl: could not resolve IP: ");
934 buffer_puts(buffer_2,tolookup);
935 buffer_putnlflush(buffer_2);
936 return 1;
937 }
938 ai=aitop;
939 while (ai) {
940 uint32_t scopeid;
941 if (ai->ai_family==AF_INET6) {
942 char* addr;
943 stralloc_catb(&ips,addr=(char*)&(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr),16);
944 scopeid=((struct sockaddr_in6*)ai->ai_addr)->sin6_scope_id;
945 if (scopeid==0 || byte_diff(addr,8,"\xfe\x80\x00\x00\x00\x00\x00\x00")) scopeid=scope_id;
946 } else {
947 stralloc_catb(&ips,V4mappedprefix,12);
948 stralloc_catb(&ips,(char*)&(((struct sockaddr_in*)ai->ai_addr)->sin_addr),4);
949 scopeid=0;
950 }
951 stralloc_catb(&ips,(char*)&scopeid,4);
952 ai=ai->ai_next;
953 }
954 if (verbose) buffer_putsflush(buffer_1,"done\n");
955 }
956 nodns:
957
958 if (output)
959 filename=strcmp(output,"-")?output:"";
960 else
961 filename=c+str_rchr(c,'/')+1;
962 if (resume || newer) {
963 struct stat ss;
964 if (stat(filename,&ss)==0) {
965 if (resume) {
966 resumeofs=ss.st_size;
967 if (verbose) {
968 buffer_puts(buffer_1,"Resuming from ");
969 buffer_putulonglong(buffer_1,resumeofs);
970 buffer_putsflush(buffer_1,"...\n");
971 }
972 } else if (newer) {
973 if (verbose) buffer_putsflush(buffer_1,"Found old file as If-Modified-Since reference.\n");
974 ims=ss.st_mtime;
975 }
976 } else
977 resume=0;
978 }
979
980 if (mode==HTTP) {
981 size_t cookielen=fmt_cookies(0,host,c);
982 size_t referlen=referer?str_len(referer)+20:0;
983 if (proxyhost) c=argv[optind];
984 request=malloc(300+str_len(host)+3*str_len(c)+str_len(useragent)+referlen+cookielen);
985
986 if (!request) panic("malloc");
987 {
988 int i;
989 if (onlyprintlocation)
990 i=fmt_str(request,"HEAD ");
991 else
992 i=fmt_str(request,"GET ");
993 i+=fmt_urlencoded(request+i,c,str_len(c));
994 i+=fmt_str(request+i," HTTP/1.0\r\nHost: ");
995 i+=fmt_str(request+i,host);
996 if (port!=80) {
997 i+=fmt_str(request+i,":");
998 i+=fmt_ulong(request+i,port);
999 }
1000 if (ims) {
1001 i+=fmt_str(request+i,"\r\nIf-Modified-Since: ");
1002 i+=fmt_httpdate(request+i,ims);
1003 }
1004 if (resumeofs) {
1005 i+=fmt_str(request+i,"\r\nRange: bytes=");
1006 i+=fmt_ulonglong(request+i,resumeofs);
1007 i+=fmt_str(request+i,"-");
1008 }
1009 i+=fmt_str(request+i,"\r\nAccept: */*\r\nUser-Agent: ");
1010 i+=fmt_str(request+i,useragent);
1011 if (referer) {
1012 i+=fmt_str(request+i,"\r\nReferer: ");
1013 i+=fmt_str(request+i,referer);
1014 }
1015 i+=fmt_str(request+i,"\r\nConnection: ");
1016 i+=fmt_str(request+i,keepalive?"keep-alive":"close");
1017 i+=fmt_str(request+i,"\r\n");
1018 i+=fmt_cookies(request+i,host,c);
1019 i+=fmt_str(request+i,"\r\n");
1020 rlen=i; request[rlen]=0;
1021 }
1022 }
1023 }
1024
1025 {
1026 int i;
1027 s=-1;
1028 for (i=0; i+20<=ips.len; i+=20) {
1029 uint32_t scopeid;
1030 if (usev4 && !ip6_isv4mapped(ips.s+i)) continue;
1031 if (verbose) {
1032 char buf[IP6_FMT];
1033 buffer_puts(buffer_1,"connecting to ");
1034 buffer_put(buffer_1,buf,fmt_ip6c(buf,ips.s+i));
1035 buffer_puts(buffer_1," port ");
1036 buffer_putulong(buffer_1,socksproxyhost?socksport:connport);
1037 buffer_putnlflush(buffer_1);
1038 }
1039 byte_copy((char*)&scopeid,4,ips.s+i+16);
1040 /* To support TCP fast open, we send the request at connect time */
1041 if (socksproxyhost)
1042 sent=3, s=make_connection(ips.s+i,socksport,scopeid,"\x05\x01\x00",&sent);
1043 else
1044 sent=rlen, s=make_connection(ips.s+i,connport,scopeid,request,&sent);
1045 if (s!=-1) {
1046 byte_copy(ip,16,ips.s+i);
1047 break;
1048 }
1049 }
1050 if (s==-1)
1051 return 1;
1052 }
1053 /* connected; if we are in socks mode, negotiate connection */
1054 if (socksproxyhost)
1055 if (negotiatesocksconnection(s,connhost,connport,externalsocksip,&externalsocksport))
1056 return 1;
1057
1058 if (mode==HTTP) {
1059 if (socksproxyhost) {
1060 if (write(s,request,rlen)!=rlen) panic("write");
1061 } else {
1062 if (sent!=rlen) panic("write");
1063 }
1064 switch (readanswer(s,filename,host,onlyprintlocation,port)) {
1065 case -1: exit(1);
1066 case -2: free(referer);
1067 referer=strdup(argv[optind]);
1068 argv[optind]=location;
1069 if (onlyprintlocation) {
1070 buffer_puts(buffer_1,location);
1071 buffer_putnlflush(buffer_1);
1072 return 0;
1073 }
1074 if (verbose) {
1075 buffer_puts(buffer_1,"redirected to ");
1076 buffer_puts(buffer_1,location);
1077 buffer_putsflush(buffer_1,"...\n");
1078 }
1079 location=0;
1080 goto again;
1081 }
1082
1083 } else if (mode==FTP) {
1084 char ip3[16];
1085 char buf[2048];
1086 int i;
1087 int srv=-1,dataconn=-1;
1088 buffer_init(&ftpbuf,(void*)read,s,buf,sizeof buf);
1089 if (verbose) buffer_putsflush(buffer_1,"Waiting for FTP greeting...");
1090 if ((readftpresponse(&ftpbuf)/100)!=2) panic("no 2xx ftp greeting.\n");
1091 if (verbose) buffer_putsflush(buffer_1,"\nUSER anonymous...");
1092 if ((i=(ftpcmd(s,&ftpbuf,"USER anonymous\r\n")/100))>3) panic("ftp login failed.\n");
1093 if (i!=2) {
1094 if (verbose) buffer_putsflush(buffer_1,"\nPASS luser@...");
1095 if ((i=(ftpcmd(s,&ftpbuf,"PASS luser@\r\n")/100))!=2) panic("ftp login failed.\n");
1096 }
1097
1098 if (verbose) buffer_putsflush(buffer_1,"\nTYPE I");
1099 if ((i=(ftpcmd(s,&ftpbuf,"TYPE I\r\n")/100))!=2) panic("Switching to binary mode failed.\n");
1100
1101 if (verbose) {
1102 buffer_puts(buffer_1,"\nMDTM ");
1103 buffer_puts(buffer_1,pathname);
1104 buffer_putsflush(buffer_1,"... ");
1105 }
1106 if (ftpcmd2(s,&ftpbuf,"MDTM ",pathname)==213) {
1107 char* c=ftpresponse.s+1;
1108 struct tm t;
1109 int ok=1;
1110 if (ftpresponse.len>15) {
1111 int i=0;
1112 if (c[0]=='1' && c[1]=='9' && c[15]>='0') {
1113 /* y2k bug; "19100" instead of "2000" */
1114 if (scan_int2digit(c+3,&i)!=2) ok=0;
1115 t.tm_year=i;
1116 ++c;
1117 } else {
1118 if (scan_int2digit(c,&i)!=2) ok=0;
1119 t.tm_year=i*100;
1120 if (scan_int2digit(c+2,&i)!=2) ok=0;
1121 t.tm_year+=i;
1122 t.tm_year-=1900;
1123 }
1124 c+=4;
1125 if (scan_int2digit(c ,&i)!=2) ok=0; t.tm_mon=i-1;
1126 if (scan_int2digit(c+2 ,&i)!=2) ok=0; t.tm_mday=i;
1127 if (scan_int2digit(c+4 ,&i)!=2) ok=0; t.tm_hour=i;
1128 if (scan_int2digit(c+6 ,&i)!=2) ok=0; t.tm_min=i;
1129 if (scan_int2digit(c+8 ,&i)!=2) ok=0; t.tm_sec=i;
1130 if (c[10]!='\r') ok=0;
1131 if (ok) {
1132 time_t r=mktime(&t);
1133 u.actime=r;
1134 if (verbose) buffer_putsflush(buffer_1,"ok.\n");
1135 if (ims && r<=ims) {
1136 if (verbose) buffer_puts(buffer_1,"Remote file is not newer, skipping download.");
1137 goto skipdownload;
1138 }
1139 } else
1140 if (verbose) buffer_putsflush(buffer_1,"could not parse MDTM response.\n");
1141 } else
1142 if (verbose) buffer_putsflush(buffer_1,"invalid response format.\n");
1143 } else
1144 if (verbose) buffer_putsflush(buffer_1,"failed.\n");
1145
1146 if (resume) {
1147 char* buf=alloca(str_len(filename)+10);
1148 int i;
1149 i=fmt_str(buf,"REST ");
1150 i+=fmt_ulonglong(buf+i,resumeofs);
1151 i+=fmt_str(buf+i,"\r\n");
1152 buf[i]=0; ++i;
1153 if (verbose) {
1154 buffer_put(buffer_1,buf,i-3);
1155 buffer_putsflush(buffer_1,"... ");
1156 }
1157 if (ftpcmd(s,&ftpbuf,buf)!=350) {
1158 buffer_putsflush(buffer_1,verbose?"FAILED!\n":"Resume failed!\n");
1159 exit(1);
1160 }
1161 }
1162
1163 if (useport) {
1164 uint16 port;
1165 char ip2[16];
1166 char buf[200];
1167 if (usev4) {
1168 int i,j;
1169 /* TODO: if (socksproxyhost) socks_bind_request (rfc1928) */
1170 srv=socket_tcp4b();
1171 if (srv==-1) panic("socket");
1172 socket_listen(srv,1);
1173 if (socket_local4(s,ip2,0)) panic("getsockname");
1174 if (socket_local4(srv,0,&port)) panic("getsockname");
1175 i=fmt_str(buf,"PORT ");
1176 for (j=0; j<4; ++j) {
1177 i+=fmt_uint(buf+i,ip2[j]&0xff);
1178 i+=fmt_str(buf+i,",");
1179 }
1180 i+=fmt_uint(buf+i,port>>8);
1181 i+=fmt_str(buf+i,",");
1182 i+=fmt_uint(buf+i,port&0xff);
1183 i+=fmt_str(buf+i,"\r\n");
1184 buf[i]=0;
1185 if (verbose) buffer_putsflush(buffer_1,buf);
1186 if (ftpcmd(s,&ftpbuf,buf) != 200) panic("PORT reply is not 200\n");
1187 } else {
1188 int i;
1189 /* TODO: if (socksproxyhost) socks_bind_request (rfc1928) */
1190 srv=socket_tcp6b();
1191 if (srv==-1) panic("socket");
1192 socket_listen(srv,1);
1193 if (socket_local6(s,ip2,0,0)) panic("getsockname");
1194 if (socket_local6(srv,0,&port,0)) panic("getsockname");
1195 i=fmt_str(buf,"EPRT |");
1196 if (byte_equal(ip2,12,V4mappedprefix))
1197 i+=fmt_str(buf+i,"1|");
1198 else
1199 i+=fmt_str(buf+i,"2|");
1200 i+=fmt_ip6c(buf+i,ip2);
1201 i+=fmt_str(buf+i,"|");
1202 i+=fmt_ulong(buf+i,port);
1203 i+=fmt_str(buf+i,"|\r\n");
1204 buf[i]=0;
1205 if (verbose) buffer_putsflush(buffer_1,buf);
1206 if (ftpcmd(s,&ftpbuf,buf) != 200) panic("EPRT reply is not 200\n");
1207 }
1208 } else {
1209 int srv;
1210 tryv4:
1211 if (usev4) {
1212 int i;
1213 if (verbose) buffer_putsflush(buffer_1,"PASV... ");
1214 if (ftpcmd(s,&ftpbuf,"PASV\r\n")!=227) panic("PASV reply is not 227\n");
1215 /* Passive Mode OK (127,0,0,1,204,228) */
1216 for (i=0; i<ftpresponse.len-1; ++i) {
1217 if (ftpresponse.s[i]==',' && ftpresponse.s[i+1]>='0' && ftpresponse.s[i+1]<='9') {
1218 unsigned long j;
1219 if (scan_ulong(ftpresponse.s+i+1,&j) && j<256)
1220 port=port*256+j;
1221 }
1222 }
1223 /* TODO: if (socksproxyhost) socks_connect (rfc1928) */
1224 if ((srv=socket_tcp4b())==-1) panic("socket");
1225 if (verbose) buffer_putsflush(buffer_1,"connecting... ");
1226 if (socket_connect4(srv,ip+12,port)==-1) panic("connect");
1227 if (verbose) buffer_putsflush(buffer_1,"done.\n");
1228 dataconn=srv;
1229 } else {
1230 if (verbose) buffer_putsflush(buffer_1,"EPSV... ");
1231 if (ftpcmd(s,&ftpbuf,"EPSV\r\n")!=229) {
1232 usev4=1;
1233 goto tryv4;
1234 panic("EPSV reply is not 229\n");
1235 }
1236 /* Passive Mode OK (|||52470|) */
1237 for (i=0; i<ftpresponse.len-1; ++i) {
1238 if (ftpresponse.s[i]>='0' && ftpresponse.s[i]<='9') {
1239 unsigned long j;
1240 if (scan_ulong(ftpresponse.s+i,&j) && j<65536) {
1241 port=j;
1242 break;
1243 }
1244 }
1245 }
1246 /* TODO: if (socksproxyhost) socks_connect (rfc1928) */
1247 if ((srv=socket_tcp6b())==-1) panic("socket");
1248 if (verbose) buffer_putsflush(buffer_1,"connecting... ");
1249 if (socket_connect6(srv,ip,port,scope_id)==-1) panic("connect");
1250 if (verbose) buffer_putsflush(buffer_1,"done.\n");
1251 dataconn=srv;
1252 }
1253 }
1254 if (!filename[0]) {
1255 if (verbose) {
1256 buffer_puts(buffer_1,"CWD ");
1257 buffer_puts(buffer_1,pathname);
1258 buffer_putsflush(buffer_1,"... ");
1259 }
1260 if ((ftpcmd2(s,&ftpbuf,"CWD ",pathname)/100)!=2) goto tryretr;
1261 if (longlist) {
1262 if (verbose) buffer_putsflush(buffer_2,"\nLIST\n");
1263 if (((i=ftpcmd(s,&ftpbuf,"LIST\r\n"))!=150) && i!=125) panic("No 125/150 response to LIST\n");
1264 } else {
1265 if (verbose) buffer_putsflush(buffer_2,"\nNLST\n");
1266 if (((i=ftpcmd(s,&ftpbuf,"NLST\r\n"))!=150) && i!=125) panic("No 125/150 response to NLST\n");
1267 }
1268 } else
1269 tryretr:
1270 {
1271 int i;
1272 if (verbose) {
1273 buffer_puts(buffer_1,"RETR ");
1274 buffer_puts(buffer_1,pathname);
1275 buffer_putsflush(buffer_1,"... ");
1276 }
1277 if (((i=ftpcmd2(s,&ftpbuf,"RETR ",pathname))!=150) && i!=125) {
1278 stralloc_0(&ftpresponse);
1279 buffer_puts(buffer_2,"dl: RETR failed:");
1280 buffer_putsaflush(buffer_2,&ftpresponse);
1281 return 1;
1282 }
1283 if (verbose) buffer_putsflush(buffer_1,"ok. Downloading...\n");
1284 total=0;
1285 if (stralloc_0(&ftpresponse)) {
1286 char* c=strchr(ftpresponse.s,'(');
1287 if (c) {
1288 ++c;
1289 if (!scan_ulonglong(c,&total))
1290 total=0;
1291 }
1292 }
1293 }
1294
1295 /* if we were in active mode, accept connection now */
1296 if (useport) {
1297 if (usev4) {
1298 if (verbose) buffer_putsflush(buffer_1,"Waiting for connection...");
1299 dataconn=socket_accept4(srv,ip3,0);
1300 if (verbose) buffer_putsflush(buffer_1," there it is.\n");
1301 if (byte_diff(ip3,4,ip+12)) panic("PORT stealing attack!\n");
1302 } else {
1303 if (verbose) buffer_putsflush(buffer_1,"Waiting for connection...");
1304 dataconn=socket_accept6(srv,ip3,0,0);
1305 if (verbose) buffer_putsflush(buffer_1," there it is.\n");
1306 if (byte_diff(ip3,16,ip)) panic("EPRT stealing attack!\n");
1307 }
1308 close(srv);
1309 }
1310
1311 {
1312 char buf[8192];
1313 unsigned int l;
1314 int64 d;
1315 if (filename[0]) {
1316 if ((resume?io_appendfile(&d,filename):io_createfile(&d,filename))==0)
1317 panic("creat");
1318 } else {
1319 d=1;
1320 dostats=!isatty(1);
1321 }
1322 while ((l=read(dataconn,buf,sizeof buf))>0) {
1323 printstats(l,d);
1324 if (d==1) {
1325 unsigned int i,j;
1326 for (i=j=0; i<l; ++i)
1327 if (buf[i]!='\r') {
1328 buf[j]=buf[i];
1329 ++j;
1330 }
1331 l=j;
1332 }
1333 if (write(d,buf,l) != l) panic("short write");
1334 }
1335 if (l==-1) panic("read");
1336 if (d!=1) close(d);
1337 }
1338 close(dataconn);
1339 if (verbose) buffer_putsflush(buffer_1,"Download finished... Waiting for server to acknowledge... ");
1340 if ((readftpresponse(&ftpbuf)/100)!=2) panic("no 2xx ftp retr response.\n");
1341 skipdownload:
1342 if (verbose) buffer_putsflush(buffer_1,"\nQUIT\n");
1343 ftpcmd(s,&ftpbuf,"QUIT\r\n");
1344
1345 } else if (mode==SMB) {
1346
1347 unsigned int mid=4;
1348 char inbuf[65*1024];
1349 char buf[8192];
1350 char* readbuf;
1351 char domain[200];
1352 size_t dlen;
1353 size_t wanted;
1354 unsigned short uid,tid,fid;
1355 size_t readsize;
1356 unsigned long long filesize;
1357 buffer ib=BUFFER_INIT(read,s,inbuf,sizeof(inbuf));
1358
1359 /* Step 1: Negotiate dialect. We only offer one */
1360 if (verbose) buffer_putsflush(buffer_1,"Negotiating SMB dialect... ");
1361 if (write(s,"\x00\x00\x00\x2f" // NetBIOS
1362 "\xffSMB" // SMB
1363 "\x72\x00\x00\x00\x00\x00\x01\xc0\x00\x00"
1364 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1365 "\x00\x00\x17\x00\x00\x00\x01\x00\x00\x0c"
1366 "\x00\x02NT LM 0.12",0x2f+4)!=0x2f+4) panic("Protocol negotiation request short write\n");
1367
1368 readnetbios(&ib,buf,&wanted);
1369
1370 if (wanted>sizeof(buf)) panic("packet too large");
1371 if (buffer_get(&ib,buf,wanted)!=wanted) panic("Protocol negotiation response short read\n");
1372 if (validatesmb(buf,wanted,0x72,17,0,0,1)) panic("Received invalid SMB response\n");
1373 if (uint16_read(buf+0x21)!=0) panic("Server requested invalid dialect\n");
1374
1375 {
1376 char* x=buf+0x20+2*17;
1377 char* max=x+3+uint16_read(x+1);
1378 x+=3+(unsigned char)x[0];
1379 if (max>x && max-x<sizeof(domain)) {
1380 dlen=max-x; // we are opportunistic bastards
1381 byte_copy(domain,dlen,x); // in session setup we claim to come from the server's workgroup
1382 if (verbose) {
1383 int i;
1384 buffer_puts(buffer_1,"ok, got domain \"");
1385 for (i=0; i<dlen; i+=2) {
1386 if (domain[i+1] || !isprint(domain[i])) {
1387 if (domain[i]==0) break;
1388 buffer_put(buffer_1,".",1);
1389 } else
1390 buffer_put(buffer_1,domain+i,1);
1391 }
1392 buffer_putsflush(buffer_1,"\".\nSession Setup... ");
1393 }
1394 } else
1395 dlen=0;
1396 }
1397
1398 if ((buf[0x33]&0x40)==0x40)
1399 readsize=64000;
1400 else {
1401 readsize=uint32_read(buf+0x27);
1402 if (readsize>64000) readsize=64000;
1403 }
1404 readbuf=malloc(readsize+300);
1405 if (!readbuf) panic("out of memory");
1406
1407 /* Step 2: Session Setup. */
1408 {
1409 char *x;
1410 static char req[300]=
1411 "\x00\x00\x00\x00" // NetBIOS
1412 "\xffSMB" // SMB
1413 "\x73\x00\x00\x00\x00\x00\x01\xc0\x00\x00"
1414 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1415 "\x00\x00\x17\x00\x00\x00\x02\x00\x0d\xff"
1416 "\x00\x00\x00\xff\xff\x02\x00\x17\x00\x17"
1417 "\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00"
1418 "\x00\x5c\x00\x00\x00"
1419 "\x00\x00" // byte count
1420 "\x00\x00\x00"
1421 "G\x00U\x00""E\x00S\x00T\x00\x00\x00"; // "GUEST"
1422 size_t i;
1423 x=req+8+50+5+2+3+6*2;
1424 if (dlen) {
1425 byte_copy(x,dlen,domain);
1426 x+=dlen;
1427 }
1428 byte_copy(x,11,"U\x00n\x00i\x00x\x00\x00\x00\x00");
1429 x+=11;
1430 for (i=0; useragent[i]; ++i) {
1431 *x++=useragent[i];
1432 *x++=0;
1433 }
1434 x[0]=x[1]=x[2]=0;
1435 x+=3;
1436 uint32_pack_big(req,x-req-4);
1437 {
1438 char* y=req+8+50+5;
1439 uint16_pack(y,x-y-2);
1440 }
1441 if (write(s,req,x-req) != x-req) panic("Session Setup request short write");
1442 }
1443
1444 readnetbios(&ib,buf,&wanted);
1445 if (wanted>sizeof(buf)) panic("packet too large");
1446 if (buffer_get(&ib,buf,wanted)!=wanted) panic("Session Setup response short read\n");
1447 if (validatesmb(buf,wanted,0x73,3,0,0,2)) panic("Received invalid SMB response\n");
1448 uid=uint16_read(buf+0x1c);
1449
1450 if (verbose) {
1451 char* x,*y, * max;
1452 x=buf+0x20;
1453 x+=1+(unsigned char)x[0]*2;
1454 max=x+2+uint16_read(x);
1455 buffer_puts(buffer_1,"ok");
1456 x+=2;
1457 if ((uintptr_t)x&1) ++x;
1458 y=x;
1459 while (y<max && *y) y+=2;
1460 y+=2;
1461 if (y<max) {
1462 buffer_puts(buffer_1,", server \"");
1463 while (y<max) {
1464 if (y[1] || !isprint(y[0])) {
1465 if (!y[0]) break;
1466 buffer_put(buffer_1,".",1);
1467 } else
1468 buffer_put(buffer_1,y,1);
1469 y+=2;
1470 }
1471 buffer_puts(buffer_1,"\" on \"");
1472 while (x<max) {
1473 if (x[1] || !isprint(x[0])) {
1474 if (!x[0]) break;
1475 buffer_put(buffer_1,".",1);
1476 } else
1477 buffer_put(buffer_1,x,1);
1478 x+=2;
1479 }
1480 }
1481 buffer_putsflush(buffer_1,"\".\nTree Connect... ");
1482 }
1483
1484 /* Step 3: Tree Connect */
1485 {
1486 char *x;
1487 char req[200+(strlen(host)+strlen(pathname))*2];
1488 size_t i;
1489 byte_copy(req,8+30+7+2+1,
1490 "\x00\x00\x00\x00" // NetBIOS
1491 "\xffSMB" // SMB
1492 "\x75\x00\x00\x00\x00\x00\x01\xc0\x00\x00"
1493 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1494 "\x00\x00\x17\x00\x00\x00\x03\x00\x04\xff"
1495 "\x00\x00\x00\x00\x00\x01\x00"
1496 "\x00\x00" // byte count
1497 "\x00");
1498 x=req+8+30+7+2+1;
1499 x[0]=x[2]='\\';
1500 x[1]=x[3]=0;
1501 x+=4;
1502 for (i=0; host[i]; ++i) {
1503 x[0]=host[i];
1504 x[1]=0;
1505 x+=2;
1506 }
1507 x[0]='\\'; x[1]=0; x+=2;
1508 if (*pathname=='/' || *pathname=='\\') ++pathname;
1509 for (i=0; pathname[i] && pathname[i]!='/' && pathname[i]!='\\'; ++i) {
1510 x[0]=pathname[i];
1511 x[1]=0;
1512 x+=2;
1513 }
1514 byte_copy(x,8,"\x00\x00?????");
1515 x+=8;
1516 uint32_pack_big(req,x-req-4);
1517 {
1518 char* y=req+8+30+7;
1519 uint16_pack(y,x-y-2);
1520 }
1521 uint16_pack(req+4+0x1c,uid);
1522 if (write(s,req,x-req) != x-req) panic("Tree Connect request short write");
1523 }
1524
1525 readnetbios(&ib,buf,&wanted);
1526 if (wanted>sizeof(buf)) panic("packet too large");
1527 if (buffer_get(&ib,buf,wanted)!=wanted) panic("Tree Connect response short read\n");
1528 tid=uint16_read(buf+24);
1529 if (validatesmb(buf,wanted,0x75,3,0,tid,3)) panic("Received invalid SMB response\n");
1530 if (verbose) {
1531 buffer_puts(buffer_1,"ok, tid=");
1532 buffer_putulong(buffer_1,tid);
1533 buffer_putsflush(buffer_1,".\nCreateFile... ");
1534 }
1535
1536 /* Step 4: CreateFile */
1537 {
1538 char *x,*y;
1539 char req[200+(strlen(pathname))*2];
1540 byte_copy(req,8+80+2,
1541 "\x00\x00\x00\x00" // NetBIOS
1542 "\xffSMB" // SMB
1543 "\xa2\x00\x00\x00\x00\x00\x01\xc0\x00\x00"
1544 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1545 "\x00\x00\x17\x00\x00\x00\x04\x00\x18\xff"
1546 "\x00\x00\x00\x00\xFE\xFE\x10\x00\x00\x00"
1547 "\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00"
1548 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1549 "\x07\x00\x00\x00\x01\x00\x00\x00\x40\x00"
1550 "\x00\x00\x01\x00\x00\x00\x01"
1551 "\x00\x00" // byte count
1552 "\x00\\\x00");
1553 uint16_pack(req+4+24,tid);
1554 uint16_pack(req+4+0x1c,uid);
1555 x=req+8+80+2;
1556 y=pathname;
1557
1558 while (*y=='/' || *y=='\\') ++y;
1559 while (*y && *y!='/' && *y!='\\') ++y;
1560 while (*y=='/' || *y=='\\') ++y;
1561
1562 uint16_pack(req+8+34,(strlen(y)+1)*2);
1563 while (*y) {
1564 x[0]=*y;
1565 if (x[0]=='/') x[0]='\\';
1566 x[1]=0;
1567 x+=2;
1568 ++y;
1569 }
1570 uint32_pack_big(req,x-req-4);
1571 {
1572 char* y=req+8+77;
1573 uint16_pack(y,x-y-2);
1574 }
1575 if (write(s,req,x-req) != x-req) panic("CreateFile request short write");
1576 }
1577 readnetbios(&ib,buf,&wanted);
1578 if (wanted>sizeof(buf)) panic("packet too large");
1579 if (buffer_get(&ib,buf,wanted)!=wanted) panic("CreateFile response short read\n");
1580 if (validatesmb(buf,wanted,0xa2,34,0,tid,4)) panic("Received invalid SMB response\n");
1581 fid=uint16_read(buf+0x20+6);
1582 filesize=uint64_read(buf+0x58);
1583 u.actime=(uint64_read(buf+0x44) / 10000000ll) - 11644473600ll;
1584 if (verbose) {
1585 char tbuf[30];
1586 tbuf[fmt_httpdate(tbuf,u.actime)]=0;
1587 buffer_putm(buffer_1,"ok, is a ",buf[0x20+68]==0?"file":"directory",", fid=");
1588 buffer_putulong(buffer_1,fid);
1589 buffer_puts(buffer_1,", size=");
1590 buffer_putulonglong(buffer_1,filesize);
1591 buffer_putmflush(buffer_1,", mtime=",tbuf,".\n");
1592 }
1593
1594 if (buf[0x20+68]==1) {
1595 // is a directory, do FindFirst/FindNext instead of ReadFile
1596
1597 time_t now;
1598 char *x,*y;
1599 char req[200+2048];
1600 char* filename=0;
1601
1602 if (strlen(pathname)>1024) panic("file name too long\n");
1603
1604 now=time(0);
1605
1606 byte_copy(req,4+78,
1607 "\x00\x00\x00\x58" // 0 NetBIOS
1608 "\xffSMB" // 4+0 SMB
1609 "\x32\x00\x00\x00\x00\x08\x01\xc0\x00\x00"
1610 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1611 "\x00\x00" // 4+24 Tree ID
1612 "\x17\x00" // 4+26 Process ID
1613 "\x00\x00" // 4+28 User ID
1614 "\x00\x00" // 4+30 Multiplex ID
1615 // Trans2 Request
1616 "\x0f" // 4+32 Word Count (15)
1617 "\x12\x00" // 4+33 Total Parameter Count (18)
1618 "\x00\x00\x0a\x00\x38\x1f\x00\x00\x00\x00"
1619 "\x00\x00\x00\x00\x00\x00"
1620 "\x12\x00" // 4+51 Parameter Count (18)
1621 "\x42\x00" // 4+53 Parameter Offset (66)
1622 "\x00\x00" // 4+55 Data Count
1623 "\x58\x00" // 4+57 Data Offset (88)
1624 "\x01\x00"
1625 "\x01\x00" // 4+61 FIND_FIRST2
1626 // word count from 4+32 points here
1627 "\x15\x00" // 4+63 Byte Count, starts counting here
1628 "\x00" // Padding
1629 // FIND_FIRST2 Parameters; 4+53 points here, parameter count from 4+33 and 4+51 starts counting here
1630 "\x17\x00" // 4+66 search attributes: +hidden +system +directory +readonly
1631 "\x56\x05" // 4+68 search count: 1366 (!?!?)
1632 "\x06\x00" // 4+70 flags: return resume keys + close on eos
1633 "\x04\x01" // 4+72 level of interest: find file both directory info (260)
1634 "\x00\x00\x00\x00"); // 4+74 storage type
1635
1636 uint16_pack(req+4+24,tid);
1637 uint16_pack(req+4+28,uid);
1638 uint16_pack(req+4+30,++mid);
1639
1640 x = req + 4+78;
1641 y = pathname;
1642
1643 {
1644 uint32_t ch;
1645 size_t i,pathlen;
1646
1647 y = pathname;
1648 while (*y=='/' || *y=='\\') ++y;
1649 while (*y && *y!='/' && *y!='\\') ++y;
1650 while (*y=='/' || *y=='\\') ++y;
1651
1652 for (i=0; y[i]; ) {
1653 size_t r;
1654 r=scan_utf8(y+i,5,&ch);
1655 if (r)
1656 y+=r;
1657 else {
1658 ch=(unsigned char)y[i];
1659 ++y;
1660 }
1661 uint16_pack(x,ch);
1662 x+=2;
1663 }
1664 if (ch!='\\') {
1665 uint16_pack(x,'\\');
1666 x+=2;
1667 }
1668 uint16_pack(x,'*');
1669 x+=2;
1670 uint16_pack(x,0);
1671 x+=2;
1672 pathlen=x-(req+4+78); // length in bytes
1673
1674 uint16_pack(req+4+63, 13+pathlen); // byte count
1675 uint16_pack(req+4+33, 12+pathlen); // total parameter count
1676 uint16_pack(req+4+51, 12+pathlen); // parameter count
1677 uint16_pack(req+4+57, 76+pathlen); // data offset
1678 uint32_pack_big(req,x-req-4);
1679 if (write(s,req,x-req) != x-req) panic("FindFirst request short write");
1680 }
1681
1682 for (;;) {
1683 int end_of_search;
1684 uint32_t fnlen=0;
1685 uint16_t search_id=0;
1686
1687 readnetbios(&ib,buf,&wanted);
1688 if (wanted>sizeof(buf)) panic("packet too large");
1689 if (buffer_get(&ib,buf,wanted)!=wanted) panic(filename?"FindNext response short read\n":"FindFirst response short read\n");
1690 if (validatesmb(buf,wanted,0x32,filename?8:10,0,tid,mid)) panic("Received invalid SMB response\n");
1691
1692 // Unfortunately, the reply does not say whether it is replying to a FIND_FIRST2 or a FIND_NEXT2
1693 // So we look at the parameter count. For FIND_FIRST2 it is 10, for FIND_NEXT2 it is 8.
1694 {
1695 char* x=buf+0x20;
1696 size_t pcount=uint16_read(x+1);
1697 size_t pofs=uint16_read(x+9);
1698 size_t dcount=uint16_read(x+13);
1699 size_t dofs=uint16_read(x+15);
1700 size_t bcount=uint16_read(x+21);
1701 if (dofs+dcount>wanted || 0x20+21+bcount>wanted)
1702 panic("SMB protocol violation: data count does not fit into packet\n");
1703 if (pofs+pcount>dofs)
1704 panic("SMB protocol violation: parameters overlap with data\n");
1705 if (pcount != uint16_read(x+7))
1706 panic("SMB protocol violation: parameter count != total parameter count\n");
1707 if (dcount != uint16_read(x+3))
1708 panic("SMB protocol violation: byte count != total data count\n");
1709 if (pofs<56)
1710 panic("SMB protocol violation: parameter offset too small\n");
1711 if (buf[0x21]==10) {
1712 search_id = uint16_read(buf+pofs);
1713 end_of_search = uint16_read(buf+pofs+4);
1714 } else
1715 end_of_search = uint16_read(buf+pofs+2);
1716 }
1717
1718 /* we got a superficially valid looking reply; dump all the file names */
1719 {
1720 char* x=buf+0x20;
1721 char* last=buf+wanted;
1722 size_t datacount = uint16_read(x+3);
1723 x = buf+uint16_read(x+15);
1724 if (x+datacount > last)
1725 panic("SMB protocol violation: data + datacount > packet\n");
1726 while (x+4<last) {
1727 time_t mtime;
1728 uint64_t filesize;
1729 uint32_t attr;
1730 uint32_t ofs=uint32_read(x);
1731 if (ofs>datacount || ofs<(120-26) || x+ofs>last)
1732 panic("SMB protocol violation: invalid ofs in filename record\n");
1733 mtime=(uint64_read(x+24) / 10000000ll) - 11644473600ll;
1734 filesize=uint64_read(x+40);
1735 attr=uint32_read(x+56);
1736 fnlen=uint32_read(x+60);
1737 filename=x+94;
1738 if (filename+fnlen > x+ofs)
1739 panic("SMB protocol violation: invalid file name length in filename record\n");
1740 {
1741 char a[FMT_ULONG];
1742 char b[100];
1743 char buf[11];
1744 size_t n;
1745 struct tm* T;
1746 static char *smonths[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
1747 char* fn;
1748 a[n=fmt_ulonglong(a,filesize)]=0;
1749 b[fmt_pad(b,a,n,10,20)]=0;
1750 T=localtime(&mtime);
1751 memset(buf,' ',sizeof(buf));
1752 fmt_num2(buf+1,T->tm_mday);
1753 if (buf[1]=='0') buf[1]=' ';
1754 if (mtime>now||now-mtime>60*60*24*365/2) {
1755 fmt_num2(buf+5,(T->tm_year+1900)/100);
1756 fmt_num2(buf+7,(T->tm_year+1900)%100);
1757 } else {
1758 fmt_num2(buf+4,T->tm_hour);
1759 buf[6]=':';
1760 fmt_num2(buf+7,T->tm_min);
1761 }
1762 buf[10]=0;
1763 if (attr&0x10)
1764 buffer_puts(buffer_1,"drwxr-xr-x");
1765 else if (attr&1)
1766 buffer_puts(buffer_1,"-r-xr-xr-x");
1767 else
1768 buffer_puts(buffer_1,"-rwxr-xr-x");
1769 buffer_putm(buffer_1," 1 root root ",b," ",smonths[T->tm_mon],buf," ");
1770 fn=filename;
1771 y=fn+fnlen;
1772 while (fn<y) {
1773 uint32_t ch=uint16_read(fn);
1774 buffer_put(buffer_1,b,fmt_utf8(b,ch));
1775 fn+=2;
1776 }
1777 buffer_putnlflush(buffer_1);
1778 }
1779 x+=ofs;
1780 }
1781 }
1782
1783 /* now see if there is a continuation or not */
1784 if (end_of_search) break;
1785
1786 /* there are more file names, we need to send a FIND_NEXT2 */
1787 if (fnlen>2048) panic("filename too long\n");
1788
1789 byte_copy(req,4+78,
1790 "\x00\x00\x00\x6c" // 0 NetBIOS
1791 "\xffSMB" // 4+0 SMB
1792 "\x32\x00\x00\x00\x00\x08\x01\xc0\x00\x00"
1793 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1794 "\x00\x00" // 4+24 Tree ID
1795 "\x17\x00" // 4+26 Process ID
1796 "\x00\x00" // 4+28 User ID
1797 "\x00\x00" // 4+30 Multiplex ID
1798 // Trans2 Request
1799 "\x0f" // 4+32 Word Count (15)
1800 "\x26\x00" // 4+33 Total Parameter Count (38)
1801 "\x00\x00\x0a\x00\x38\x1f\x00\x00\x00\x00"
1802 "\x00\x00\x00\x00\x00\x00"
1803 "\x26\x00" // 4+51 Parameter Count (38)
1804 "\x42\x00" // 4+53 Parameter Offset (66)
1805 "\x00\x00" // 4+55 Data Count
1806 "\x6c\x00" // 4+57 Data Offset (108)
1807 "\x01\x00"
1808 "\x02\x00" // 4+61 FIND_NEXT2
1809 // word count from 4+32 points here
1810 "\x2b\x00" // 4+63 Byte Count, starts counting here
1811 "\x00" // Padding
1812 // FIND_FIRST2 Parameters; 4+53 points here, parameter count from 4+33 and 4+51 starts counting here
1813 "\x01\x00" // 4+66 search id (comes from FIND_FIRST2 response)
1814 "\x56\x05" // 4+68 search count: 1366 (!?!?)
1815 "\x04\x01" // 4+70 level of interest: find file both directory info (260)
1816 "\x00\x00\x00\x00" // 4+72 resume key
1817 "\x06\x00"); // 4+76 flags
1818
1819 uint16_pack(req+4+24,tid);
1820 uint16_pack(req+4+28,uid);
1821 uint16_pack(req+4+30,++mid);
1822 uint16_pack(req+4+66,search_id);
1823
1824 x=req+4+78; byte_copy(x,fnlen,filename);
1825 x+=fnlen; byte_copy(x,2,"\x00\x00");
1826 x+=2;
1827
1828 uint16_pack(req+4+63, 13+fnlen+2); // byte count
1829 uint16_pack(req+4+33, 12+fnlen+2); // total parameter count
1830 uint16_pack(req+4+51, 12+fnlen+2); // parameter count
1831 uint16_pack(req+4+57, 76+fnlen+2); // data offset
1832 uint32_pack_big(req,x-req-4);
1833 if (write(s,req,x-req) != x-req) panic("FindNext request short write");
1834
1835 }
1836
1837 goto closeanddone;
1838 }
1839
1840 if (filesize<=resumeofs) {
1841 if (verbose) buffer_putsflush(buffer_1,"File already fully transmitted.\n");
1842 goto closeanddone;
1843 }
1844 if (ims && u.actime<=ims) {
1845 if (verbose) buffer_putsflush(buffer_1,"The local file is as new as the remote file.\n");
1846 goto closeanddone;
1847 }
1848
1849 /* Step 5: ReadFile */
1850 {
1851 static char req[]=
1852 "\x00\x00\x00\x3b" // NetBIOS
1853 "\xffSMB" // SMB
1854 "\x2e\x00\x00\x00\x00\x00\x01\xc0\x00\x00"
1855 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1856 "\x00\x00\x17\x00\x00\x00\x05\x00\x0c\xff"
1857 "\x00\x00\x00w0u0__\x00"
1858 "\xf0\x00\xf0\x00\x00\x00\x00\x00\xf0u"
1859 "1__\x00\x00";
1860 size_t rest;
1861 size_t gotten;
1862 unsigned long long curofs=resumeofs;
1863 int nextwritten=0;
1864 int64 d;
1865 uint16_pack(req+4+0x1c,uid);
1866 uint16_pack(req+4+24,tid);
1867 uint16_pack(req+8+33,fid);
1868 if (filename[0]) {
1869 if ((resume?io_appendfile(&d,filename):io_createfile(&d,filename))==0)
1870 panic("creat");
1871 } else {
1872 d=1;
1873 dostats=!isatty(1);
1874 }
1875 total=filesize-resumeofs;
1876 while (curofs<filesize) {
1877 size_t dataofs;
1878
1879 uint16_pack(req+30+4,++mid);
1880 uint32_pack(req+8+33+2,resumeofs&0xffffffff);
1881 uint32_pack(req+8+49,resumeofs>>32);
1882 rest=(filesize-curofs>readsize)?readsize:filesize-curofs;
1883 uint16_pack(req+8+33+2+4,rest);
1884 uint16_pack(req+8+33+2+6,rest);
1885 uint16_pack(req+8+47,rest);
1886
1887 if (!nextwritten) {
1888 if (write(s,req,0x3b+4)!=0x3b+4) panic("ReadFile request short write");
1889 }
1890 readnetbios(&ib,buf,&wanted);
1891 if (wanted>readsize+300) panic("packet too large");
1892 if (wanted<0x20+12*2+3) panic("SMB (ReadFile): Received invalid SMB response\n");
1893 if (buffer_get(&ib,readbuf,0x20+12*2+3)!=0x20+12*2+3) panic("ReadFile response short read\n");
1894
1895 if (validatesmb(readbuf,wanted,0x2e,12,0,tid,mid)) panic("SMB (ReadFile): Received invalid SMB response\n");
1896 gotten=uint16_read(readbuf+0x39);
1897 dataofs=uint16_read(readbuf+0x2d);
1898 if (dataofs+gotten>wanted) panic("invalid dataofs in ReadFile response");
1899 if (gotten<rest) break; // someone truncated the file while we read?
1900
1901 /* pipeline next read request */
1902 curofs+=gotten;
1903 if (curofs<filesize) {
1904 uint16_pack(req+30+4,mid+1);
1905 uint32_pack(req+8+33+2,curofs&0xffffffff);
1906 uint32_pack(req+8+49,curofs>>32);
1907 rest=(filesize-curofs>readsize)?readsize:filesize-curofs;
1908 uint16_pack(req+8+33+2+4,rest);
1909 uint16_pack(req+8+33+2+6,rest);
1910 uint16_pack(req+8+47,rest);
1911 if (write(s,req,0x3b+4)!=0x3b+4) panic("ReadFile request short write");
1912 nextwritten=1;
1913 }
1914
1915 if (buffer_get(&ib,readbuf+0x20+12*2+3,wanted-(0x20+12*2+3))!=wanted-(0x20+12*2+3)) panic("ReadFile response short read\n");
1916 if (write(d,readbuf+dataofs,gotten)!=gotten) panic("short write. disk full?\n");
1917 printstats(gotten,d);
1918 }
1919
1920 io_close(d);
1921 }
1922
1923 closeanddone:
1924
1925 if (verbose) buffer_putsflush(buffer_1,"Close... ");
1926
1927 /* Step 6: Close */
1928 {
1929 static char req[]=
1930 "\x00\x00\x00\x29" // NetBIOS
1931 "\xffSMB" // SMB
1932 "\x04\x00\x00\x00\x00\x00\x01\xc0\x00\x00"
1933 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1934 "\xFE\xFE\x17\x00\x00\x00\x05\x00\x03\xFE"
1935 "\xFE\xff\xff\xff\xff\x00\x00";
1936 uint16_pack(req+30+4,++mid);
1937 uint16_pack(req+8+29,fid);
1938 uint16_pack(req+8+20,tid);
1939 uint16_pack(req+4+0x1c,uid);
1940 if (write(s,req,8+37)!=8+37) panic("Close request short write");
1941 readnetbios(&ib,buf,&wanted);
1942 if (wanted>sizeof(buf)) panic("packet too large");
1943 if (buffer_get(&ib,buf,wanted)!=wanted) panic("Close response short read\n");
1944 if (validatesmb(buf,wanted,0x04,0,0,tid,mid)) panic("SMB (CloseFile): Received invalid SMB response\n");
1945 }
1946
1947 if (verbose) buffer_putsflush(buffer_1,"ok.\nTree Disconnect... ");
1948
1949 /* Step 7: Tree Disconnect */
1950 {
1951 static char req[]=
1952 "\x00\x00\x00\x23" // NetBIOS
1953 "\xffSMB" // SMB
1954 "\x71\x00\x00\x00\x00\x00\x01\xc0\x00\x00"
1955 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1956 "\x00\x00\x17\x00\x00\x00\x05\x00\x00\x00";
1957 uint16_pack(req+30+4,++mid);
1958 uint16_pack(req+8+33,fid);
1959 uint16_pack(req+28,tid);
1960 uint16_pack(req+4+0x1c,uid);
1961 if (write(s,req,0x23+4)!=0x23+4) panic("Tree Disconnect request short write");
1962 readnetbios(&ib,buf,&wanted);
1963 if (wanted>sizeof(buf)) panic("packet too large");
1964 if (buffer_get(&ib,buf,wanted)!=wanted) panic("Tree Disconnect response short read\n");
1965 if (validatesmb(buf,wanted,0x71,0,0,tid,mid)) panic("SMB (Tree Disconnect): Received invalid SMB response\n");
1966 }
1967 if (verbose) buffer_putsflush(buffer_1,"ok.\n");
1968
1969 } else
1970 panic("invalid mode\n");
1971 close(s);
1972 if (filename[0] && u.actime) {
1973 u.modtime=u.actime;
1974 if (strcmp(filename,"-") && utime(filename,&u)==-1)
1975 if (errno!=ENOENT || !imode)
1976 panic("utime");
1977 }
1978 clearstats();
1979 return 0;
1980 }
1981