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