#include #include #include #include #include <9p.h> #include "cifs.h" static Pkt * t2hdr(Session *s, Share *sp, int cmd) { Pkt *p; p = cifshdr(s, sp, SMB_COM_TRANSACTION2); p->tbase = pl16(p, 0); /* 0 Total parameter bytes to be sent, filled later */ pl16(p, 0); /* 2 Total data bytes to be sent, filled later */ pl16(p, 64); /* 4 Max parameter to return */ pl16(p, (MTU - T2HDRLEN)-64); /* 6 Max data to return */ p8(p, 0); /* 8 Max setup count to return */ p8(p, 0); /* 9 Reserved */ pl16(p, 0); /* 10 Flags */ pl32(p, 1000); /* 12 Timeout (ms) */ pl16(p, 0); /* 16 Reserved */ pl16(p, 0); /* 18 Parameter count, filled later */ pl16(p, 0); /* 20 Parameter offset, filled later */ pl16(p, 0); /* 22 Data count, filled later */ pl16(p, 0); /* 24 Data offset, filled later */ p8(p, 1); /* 26 Setup count (in words) */ p8(p, 0); /* 27 Reserved */ pl16(p, cmd); /* setup[0] */ pbytes(p); p8(p, 0); /* padding ??!?!? */ return p; } static void pt2param(Pkt *p) { uchar *pos = p->pos; assert(p->tbase != 0); p->pos = p->tbase + 20; pl16(p, (pos - p->buf) - NBHDRLEN); /* param offset */ p->tparam = p->pos = pos; } static void pt2data(Pkt *p) { uchar *pos = p->pos; assert(p->tbase != 0); assert(p->tparam != 0); p->pos = p->tbase +0; pl16(p, pos - p->tparam); /* total param count */ p->pos = p->tbase +18; pl16(p, pos - p->tparam); /* param count */ p->pos = p->tbase +24; pl16(p, (pos - p->buf) - NBHDRLEN); /* data offset */ p->tdata = p->pos = pos; } static int t2rpc(Pkt *p) { int got; uchar *pos; assert(p->tbase != 0); assert(p->tdata != 0); pos = p->pos; p->pos = p->tbase +2; pl16(p, pos - p->tdata); /* total data count */ p->pos = p->tbase +22; pl16(p, pos - p->tdata); /* data count */ p->pos = pos; if((got = cifsrpc(p)) == -1) return -1; gl16(p); /* Total parameter count */ gl16(p); /* Total data count */ gl16(p); /* Reserved */ gl16(p); /* Parameter count in this buffer */ p->tparam = p->buf +NBHDRLEN +gl16(p); /* Parameter offset */ gl16(p); /* Parameter displacement */ gl16(p); /* Data count (this buffer); */ p->tdata = p->buf +NBHDRLEN +gl16(p); /* Data offset */ gl16(p); /* Data displacement */ g8(p); /* Setup count */ g8(p); /* Reserved */ return got; } static void gt2param(Pkt *p) { p->pos = p->tparam; } static void gt2data(Pkt *p) { p->pos = p->tdata; } int T2findfirst(Session *s, Share *sp, int slots, char *path, int *got, long *resume, FInfo *fip) { int pktlen, i, n, sh; uchar *next; Pkt *p; p = t2hdr(s, sp, TRANS2_FIND_FIRST2); p8(p, 'D'); /* OS/2 */ p8(p, ' '); /* OS/2 */ pt2param(p); pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* Search attributes */ pl16(p, slots); /* Search count */ pl16(p, CIFS_SEARCH_RETURN_RESUME); /* Flags */ pl16(p, SMB_FIND_FILE_FULL_DIRECTORY_INFO); /* Information level */ pl32(p, 0); /* SearchStorage type (?) */ ppath(p, path); /* path */ pt2data(p); if((pktlen = t2rpc(p)) == -1){ free(p); return -1; } s->lastfind = nsec(); gt2param(p); sh = gl16(p); /* Sid (search handle) */ *got = gl16(p); /* number of slots received */ gl16(p); /* End of search flag */ gl16(p); /* Offset into EA list if EA error */ gl16(p); /* Offset into data to file name of last entry */ gt2data(p); memset(fip, 0, slots * sizeof(FInfo)); for(i = 0; i < *got; i++){ next = p->pos; next += gl32(p); /* offset to next entry */ /* * bug in Windows - somtimes it lies about how many * directory entries it has put in the packet */ if(next - p->buf > pktlen){ *got = i; break; } *resume = gl32(p); /* resume key for search */ fip[i].created = gvtime(p); /* creation time */ fip[i].accessed = gvtime(p); /* last access time */ fip[i].written = gvtime(p); /* last written time */ fip[i].changed = gvtime(p); /* change time */ fip[i].size = gl64(p); /* file size */ gl64(p); /* bytes allocated */ fip[i].attribs = gl32(p); /* extended attributes */ n = gl32(p); /* name length */ gl32(p); /* EA size */ gstr(p, fip[i].name, n); /* name */ p->pos = next; } free(p); return sh; } int T2findnext(Session *s, Share *sp, int slots, char *path, int *got, long *resume, FInfo *fip, int sh) { Pkt *p; int i, n; uchar *next; /* * So I believe from comp.protocols.smb if you send * TRANS2_FIND_NEXT2 requests too quickly to windows 95, it can * get confused and fail to reply, so we slow up a bit in these * circumstances. */ if(!(s->caps & CAP_NT_SMBS) && nsec() - s->lastfind < 200000000LL) sleep(200); p = t2hdr(s, sp, TRANS2_FIND_NEXT2); p8(p, 'D'); /* OS/2 */ p8(p, ' '); /* OS/2 */ pt2param(p); pl16(p, sh); /* search handle */ pl16(p, slots); /* Search count */ pl16(p, SMB_FIND_FILE_FULL_DIRECTORY_INFO); /* Information level */ pl32(p, *resume); /* resume key */ pl16(p, CIFS_SEARCH_CONTINUE_FROM_LAST); /* Flags */ ppath(p, path); /* file+path to resume */ pt2data(p); if(t2rpc(p) == -1){ free(p); return -1; } s->lastfind = nsec(); gt2param(p); *got = gl16(p); /* number of slots received */ gl16(p); /* End of search flag */ gl16(p); /* Offset into EA list if EA error */ gl16(p); /* Offset into data to file name of last entry */ gt2data(p); memset(fip, 0, slots * sizeof(FInfo)); for(i = 0; i < *got; i++){ next = p->pos; next += gl32(p); /* offset to next entry */ *resume = gl32(p); /* resume key for search */ fip[i].created = gvtime(p); /* creation time */ fip[i].accessed = gvtime(p); /* last access time */ fip[i].written = gvtime(p); /* last written time */ fip[i].changed = gvtime(p); /* change time */ fip[i].size = gl64(p); /* file size */ gl64(p); /* bytes allocated */ fip[i].attribs = gl32(p); /* extended attributes */ n = gl32(p); /* name length */ gl32(p); /* EA size */ gstr(p, fip[i].name, n); /* name */ p->pos = next; } free(p); return 0; } /* supported by 2k/XP/NT4 */ int T2queryall(Session *s, Share *sp, char *path, FInfo *fip) { int n; Pkt *p; p = t2hdr(s, sp, TRANS2_QUERY_PATH_INFORMATION); pt2param(p); pl16(p, SMB_QUERY_FILE_ALL_INFO); /* Information level */ pl32(p, 0); /* reserved */ ppath(p, path); /* path */ pt2data(p); if(t2rpc(p) == -1){ free(p); return -1; } gt2data(p); /* * The layout of this struct is wrong in the SINA * document, this layout gained by inspection. */ memset(fip, 0, sizeof(FInfo)); fip->created = gvtime(p); /* creation time */ fip->accessed = gvtime(p); /* last access time */ fip->written = gvtime(p); /* last written time */ fip->changed = gvtime(p); /* change time */ fip->attribs = gl32(p); /* attributes */ gl32(p); /* reserved */ gl64(p); /* bytes allocated */ fip->size = gl64(p); /* file size */ gl32(p); /* number of hard links */ g8(p); /* delete pending */ g8(p); /* is a directory */ gl16(p); /* reserved */ gl32(p); /* EA size */ n = gl32(p); if(n >= sizeof fip->name) n = sizeof fip->name - 1; gstr(p, fip->name, n); free(p); return 0; } /* supported by 95/98/ME */ int T2querystandard(Session *s, Share *sp, char *path, FInfo *fip) { Pkt *p; p = t2hdr(s, sp, TRANS2_QUERY_PATH_INFORMATION); pt2param(p); pl16(p, SMB_INFO_STANDARD); /* Information level */ pl32(p, 0); /* reserved */ ppath(p, path); /* path */ pt2data(p); if(t2rpc(p) == -1){ free(p); return -1; } gt2data(p); memset(fip, 0, sizeof(FInfo)); fip->created = gdatetime(p); /* creation time */ fip->accessed = gdatetime(p); /* last access time */ fip->written = gdatetime(p); /* last written time */ fip->changed = fip->written; /* change time */ fip->size = gl32(p); /* file size */ gl32(p); /* bytes allocated */ fip->attribs = gl16(p); /* attributes */ gl32(p); /* EA size */ free(p); return 0; } int T2setpathinfo(Session *s, Share *sp, char *path, FInfo *fip) { int rc; Pkt *p; p = t2hdr(s, sp, TRANS2_SET_PATH_INFORMATION); pt2param(p); pl16(p, SMB_INFO_STANDARD); /* Information level */ pl32(p, 0); /* reserved */ ppath(p, path); /* path */ pt2data(p); pdatetime(p, fip->created); /* created */ pdatetime(p, fip->accessed); /* accessed */ pdatetime(p, fip->written); /* written */ pl32(p, fip->size); /* size */ pl32(p, 0); /* allocated */ pl16(p, fip->attribs); /* attributes */ pl32(p, 0); /* EA size */ pl16(p, 0); /* reserved */ rc = t2rpc(p); free(p); return rc; } int T2setfilelength(Session *s, Share *sp, int fh, FInfo *fip) /* FIXME: maybe broken, needs testing */ { int rc; Pkt *p; p = t2hdr(s, sp, TRANS2_SET_FILE_INFORMATION); pt2param(p); pl16(p, fh); /* file handle */ pl16(p, SMB_SET_FILE_END_OF_FILE_INFO); /* Information level */ pl16(p, 0); /* reserved */ pt2data(p); pl64(p, fip->size); pl32(p, 0); /* padding ?! */ pl16(p, 0); rc = t2rpc(p); free(p); return rc; } int T2fsvolumeinfo(Session *s, Share *sp, long *created, long *serialno, char *label, int labellen) { Pkt *p; long ct, sn, n; p = t2hdr(s, sp, TRANS2_QUERY_FS_INFORMATION); pt2param(p); pl16(p, SMB_QUERY_FS_VOLUME_INFO); /* Information level */ pt2data(p); if(t2rpc(p) == -1){ free(p); return -1; } gt2data(p); ct = gvtime(p); /* creation time */ sn = gl32(p); /* serial number */ n = gl32(p); /* label name length */ g8(p); /* reserved */ g8(p); /* reserved */ memset(label, 0, labellen); if(n < labellen && n > 0) gstr(p, label, n); /* file system label */ if(created) *created = ct; if(serialno) *serialno = sn; free(p); return 0; } int T2fssizeinfo(Session *s, Share *sp, uvlong *total, uvlong *unused) { Pkt *p; uvlong t, f, n, b; p = t2hdr(s, sp, TRANS2_QUERY_FS_INFORMATION); pt2param(p); pl16(p, SMB_QUERY_FS_SIZE_INFO); /* Information level */ pt2data(p); if(t2rpc(p) == -1){ free(p); return -1; } gt2data(p); t = gl64(p); /* total blocks */ f = gl64(p); /* free blocks */ n = gl32(p); /* sectors per block */ b = gl32(p); /* bytes per sector */ if(free) *unused = f * n * b; if(total) *total = t * n * b; free(p); return 0; } int T2getdfsreferral(Session *s, Share *sp, char *path, int *gflags, int *used, Refer *re, int nent) { int i, vers, nret, len; char tmp[1024]; uchar *base; Pkt *p; p = t2hdr(s, sp, TRANS2_GET_DFS_REFERRAL); pt2param(p); pl16(p, 3); /* max info level we understand, must be >= 3 for domain requests */ ppath(p, path); pt2data(p); if(t2rpc(p) == -1){ free(p); return -1; } memset(re, 0, sizeof *re * nent); gt2data(p); *used = gl16(p) / 2; /* length used (/2 as Windows counts in runes) */ nret = gl16(p); /* number of referrals returned */ *gflags = gl32(p); /* global flags */ for(i = 0; i < nret && i < nent && i < 16; i++){ base = p->pos; vers = gl16(p); /* version of records */ len = gl16(p); /* length of records */ re[i].type = gl16(p); /* server type */ re[i].flags = gl16(p); /* referal flags */ switch(vers){ case 1: re[i].prox = 0; /* nearby */ re[i].ttl = 5*60; /* 5 mins */ gstr(p, tmp, sizeof tmp); re[i].addr = estrdup9p(tmp); re[i].path = estrdup9p(tmp); break; case 2: re[i].prox = gl32(p); /* not implemented in v2 */ re[i].ttl = gl32(p); goff(p, base, re[i].path, sizeof tmp); re[i].path = estrdup9p(tmp); goff(p, base, re[i].path, sizeof tmp);/* spurious 8.3 path */ goff(p, base, tmp, sizeof tmp); re[i].addr = estrdup9p(tmp); break; case 3: if(re[i].flags & DFS_REFERAL_LIST){ re[i].prox = 0; re[i].ttl = gl32(p); goff(p, base, tmp, sizeof tmp); re[i].path = estrdup9p(tmp); gl16(p); goff(p, base, tmp, sizeof tmp); re[i].addr = estrdup9p(tmp); } else{ re[i].prox = 0; re[i].ttl = gl32(p); goff(p, base, tmp, sizeof tmp); re[i].path = estrdup9p(tmp); gl16(p); /* spurious 8.3 path */ goff(p, base, tmp, sizeof tmp); re[i].addr = estrdup9p(tmp); gl16(p); /* GUID (historic) */ } break; default: /* * this should never happen as we specify our maximum * understood level in the request (above) */ fprint(2, "%d - unsupported DFS infolevel\n", vers); re[i].path = estrdup9p(tmp); re[i].addr = estrdup9p(tmp); break; } p->pos = base+len; } free(p); return i; }