#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OPAQUEDUMP_MAXLEN 4096 #define RPC_MAXLEN 8192 #define RPCTCP_TIMEOUT 20 const int localport=5111; const int pmapport=111; int hport; const char *rpcduser="rpcd"; /* structure containing all info we need about a given request */ struct inst { int seq; int *pos; int *endp; int xid; unsigned int prog; unsigned int proc; int vers; void (*sendfunc)(int, struct sockaddr *, int, void *, int); int fd; struct sockaddr *addr; int addrlen; }; /* Set check_vers to 1 if we want to reply with an error if the versions don't match. This may cause the client to try again with something that we can analyse. */ /* structure defining information about services we know about */ struct serv { int check_vers; unsigned int prog; int low, high; const char *name; void (*handler)(struct serv *, struct inst *); }; struct serv pmap_serv_table[]; void *pmap_dump_reply; int pmap_dump_reply_len; const char *progname="pmapmon"; /* JSP given a prog return table entry structure */ struct serv *getservice(unsigned int prog) { int i; for (i=0; pmap_serv_table[i].prog; i++) { if (pmap_serv_table[i].prog==prog) { return &pmap_serv_table[i]; } } return &pmap_serv_table[i]; /* last entry in table is catch all */ } void setprogname(const char *name) { char *slash=strrchr(name, '/'); progname=slash? slash+1: name; } void swarn(int seq, const char *msg, ...) { va_list al; va_start(al, msg); printf("%i ", seq); vprintf(msg, al); va_end(al); } void sdie(int seq, const char *msg, ...) { va_list al; va_start(al, msg); printf("%i ", seq); vprintf(msg, al); va_end(al); exit(1); } /* JSP given a serv, inst return suitable string */ const char *getprogname(struct serv *s, struct inst *i) { static char namebuf[32]; if (s->name[0]) { return s->name ; } else { sprintf(namebuf, "PROG(%i)", i->prog); return namebuf; } } char *inaddr(char *str, struct sockaddr_in *addr) { sprintf(str, "%i.%i.%i.%i:%i", ((unsigned char *)&addr->sin_addr)[0], ((unsigned char *)&addr->sin_addr)[1], ((unsigned char *)&addr->sin_addr)[2], ((unsigned char *)&addr->sin_addr)[3], ntohs(addr->sin_port)); return str; } void dump(int seq, void *p, int len) { unsigned char *cp; int x=0; int offset=0; char buf[17]; buf[16]=0; for (cp=p; len; len--,x++,offset++) { if (x==16) { x=0; printf(" %s\n", buf); } if (x==8) printf(" -"); if (!x) printf("%i %05x ", seq, offset); buf[x]=(32<=*cp && *cp<127)? *cp:'.'; printf(" %02x", *cp++); } buf[x]=0; if (x) printf("%*s %s\n", (16-x)*3+(x<=8?2:0), "", buf); } void decode_pmap(struct inst *i) { int *p=i->pos; printf("%i", i->seq); if (p+1<=i->endp) printf(" pm_prog=%i", ntohl(*p++)); if (p+1<=i->endp) printf(" pm_vers=%i", ntohl(*p++)); if (p+1<=i->endp) printf(" pm_prot=%i", ntohl(*p++)); if (p+1<=i->endp) printf(" pm_port=%i", ntohl(*p++)); printf("\n"); i->pos=p; } void decode_int(struct inst *i, const char *name) { int *p=i->pos; printf("%i", i->seq); if (p+1<=i->endp) printf("%i %s=%i", i->seq, name, ntohl(*p++)); printf("\n"); i->pos=p; } void decode_uint(struct inst *i, const char *name) { int *p=i->pos; printf("%i", i->seq); if (p+1<=i->endp) printf("%i %s=%u", i->seq, name, (unsigned int)ntohl(*p++)); printf("\n"); i->pos=p; } void decode_opaque_variable(struct inst *i, const char *name, int bound) { int *p=i->pos; int len, maxlen; if (p+1<=i->endp) { printf("%i %s_len=%i\n", i->seq, name, len=ntohl(*p++)); maxlen=(void *)(i->endp)-(void *)p; if (len>bound && bound!=-1) printf("%i Length of %s (%i) exceeds bound (%i)\n", i->seq, name, len, bound); if (len>maxlen) len=maxlen; dump(i->seq, p, len>OPAQUEDUMP_MAXLEN? OPAQUEDUMP_MAXLEN:len); p+=(len+3)>>2; } i->pos=p; } void decode_opaque_fixed(struct inst *i, const char *name, int len) { int *p=i->pos; int maxlen; if (p+1<=i->endp) { maxlen=(void *)i->endp-(void *)p; printf("%i %s\n", i->seq, name); if (len>maxlen) len=maxlen; dump(i->seq, p, len>OPAQUEDUMP_MAXLEN? OPAQUEDUMP_MAXLEN:len); p+=(len+3)>>2; } i->pos=p; } void rpcverserror(struct inst *i) { if (i->sendfunc) { struct reply_struct { int xid, dir, replystat, rejectstat, low, high; } reply={i->xid, htonl(1), htonl(1), 0, htonl(2), htonl(2)}; printf("%i RPC version error - Sending reject - " "want rpc version 2\n", i->seq); (*(i->sendfunc))(i->fd, i->addr, i->addrlen, &reply, sizeof(reply)); } } void progverserror(struct serv *s, struct inst *i) { if (i->sendfunc) { struct reply_struct { int xid, dir, replystat, verftype, verflen, acceptstat; int low, high; } reply={i->xid, htonl(1), 0, 0, 0, htonl(2), htonl(s->low), htonl(s->high)}; printf("%i Program version error - Sending reject - " "got vers %i but want between %i and %i\n", i->seq, i->vers, s->low, s->high); (*(i->sendfunc))(i->fd, i->addr, i->addrlen, &reply, sizeof(reply)); } } void handle_procnull(struct serv *s, struct inst *i) { if (i->sendfunc && i->pos <= i->endp) { struct reply_struct { int xid, dir, replystat, verftype, verflen, acceptstat; } reply={i->xid, htonl(1), 0, 0, 0, 0}; if (s->low<=i->vers && i->vers<=s->high) (*(i->sendfunc))(i->fd, i->addr, i->addrlen, &reply, sizeof(reply)); else progverserror(s, i); } } void handle_generic(struct serv *s, struct inst *i) { if (s->check_vers && (i->vers< s->low || i->vers> s->high)) { progverserror(s, i); return; } if (i->proc==0) { /* Don't increase high too much or 'rpcinfo [-t | -u] program_number' will send a lot of packets! */ printf("%i %s_PROCNULL - Sending reply\n", i->seq, getprogname(s, i)); handle_procnull(s, i); } else { printf("%i %s_PROC(%i)\n", i->seq, getprogname(s, i), i->proc); } dump(i->seq, i->pos, (void *)(i->endp)-(void *)(i->pos)); i->pos=i->endp; } void pmap_getport(struct inst *i) { int *p=i->pos; decode_pmap(i); if (i->sendfunc && p+4<=i->endp) { struct reply_struct { int xid, dir, replystat, verftype, verflen, acceptstat; int port; } reply={i->xid, htonl(1), 0, 0, 0, 0, hport}; printf("%i Sending reply\n", i->seq); (*(i->sendfunc))(i->fd, i->addr, i->addrlen, &reply, sizeof(reply)); } } void pmap_dump(struct inst *i) { int *p=i->pos; if (i->sendfunc && p<=i->endp) { *((int *)pmap_dump_reply)=i->xid; printf("%i Sending reply\n", i->seq); (*(i->sendfunc))(i->fd, i->addr, i->addrlen, pmap_dump_reply, pmap_dump_reply_len); } } void decode_string_variable(struct inst *i, const char *name, int bound) { int *p=i->pos; int len, maxlen; if (p+1 <= i->endp) { printf("%i %s_len=%i\n", i->seq, name, len=ntohl(*p++)); maxlen=(void *)(i->endp)-(void *)p; if (len>bound && bound!=-1) printf("%i Length of %s (%i) exceeds bound (%i)\n", i->seq, name, len, bound); if (len>maxlen) len=maxlen; dump(i->seq, p, len); p+=(len+3)>>2; } i->pos=p; } void decode_dirpath(struct inst *i) { decode_string_variable(i, "dirpath", 1024); } void decode_domainname(struct inst *i) { decode_string_variable(i, "domainname", 64); } void decode_mapname(struct inst *i) { decode_string_variable(i, "mapname", 64); } void decode_peername(struct inst *i) { decode_string_variable(i, "peername", 64); } void decode_keydat(struct inst *i) { decode_opaque_variable(i, "keydat", 1024); } void decode_ypreq_key(struct inst *i) { decode_domainname(i); decode_mapname(i); decode_keydat(i); } void decode_ypreq_nokey(struct inst *i) { decode_domainname(i); decode_mapname(i); } void decode_ypmap_parms(struct inst *i) { int *p; decode_domainname(i); decode_mapname(i); p=i->pos; if (p+1<=i->endp) printf("%i ordernum=%u", i->seq, ntohl(*p++)); i->pos = p; } void decode_ypreq_xfr(struct inst *i) { int *p; decode_ypmap_parms(i); decode_peername(i); p=i->pos; printf("%i\n", i->seq); if (p+1<=i->endp) printf(" transid=%u", ntohl(*p++)); if (p+1<=i->endp) printf(" prog=%i", ntohl(*p++)); if (p+1<=i->endp) printf(" port=%i", ntohl(*p++)); printf("\n"); i->pos=p; } void decode_ypbind_setdom(struct inst *i) { int *p; decode_domainname(i); p=i->pos; if (p+2<=i->endp) { struct sockaddr_in addr; char addrstr[22]; memcpy(&addr.sin_addr, p, 6); printf("%i ypsetdom_binding=%s\n", i->seq, inaddr(addrstr, &addr)); p+=2; } if (p+1<=i->endp) printf("%i ypsetdomvers=%u\n", i->seq, ntohl(*p++)); i->pos=p; } void mount_export(struct inst *i) { int *p=i->pos; if (i->sendfunc && p<=i->endp) { struct reply_struct { int xid, dir, replystat, verftype, verflen, acceptstat; int item1; int mntlen; char mntdir[4]; int item1a; int grouplen; char group[12]; int item1b; int item2; } reply={i->xid, htonl(1), 0, 0, 0, 0, htonl(1), htonl(1), "/\0\0", htonl(1), htonl(10), "(everyone)", 0, 0 }; printf("%i Sending reply\n", i->seq); (*(i->sendfunc))(i->fd, i->addr, i->addrlen, &reply, sizeof(reply)); } } void mount_mnt(struct inst *i) { decode_dirpath(i); if (i->sendfunc && i->pos <= i->endp) { struct reply_struct { int xid, dir, replystat, verftype, verflen, acceptstat; int err; } reply={i->xid, htonl(1), 0, 0, 0, 0, htonl(1) }; printf("%i Sending reply\n", i->seq); (*(i->sendfunc))(i->fd, i->addr, i->addrlen, &reply, sizeof(reply)); } } /* YPBIND decoder - replies to nothing - NOT PROPERLY TESTED*/ void handle_ypbind(struct serv *s, struct inst *i) { const char *procname[]={ "NULL", "DOMAIN", "SETDOM"}; if (i->proc<3) printf("%i YPBINDPROC%s\n", i->seq, procname[i->proc]); else printf("%i YPBINDPROC%i\n", i->seq, i->proc); switch(i->proc) { case 0: handle_procnull(s, i); return; case 1: decode_domainname(i); return; case 2: decode_ypbind_setdom(i); return; } } /* YP decoder - replies to nothing */ void handle_yp(struct serv *s, struct inst *i) { const char *procname[]={ "NULL", "DOMAIN", "DOMAIN_NONACK", "MATCH", "FIRST", "NEXT", "XFR", "CLEAR", "ALL", "MASTER", "ORDER", "MAPLIST"}; if (i->proc<=11) printf("%i YPPROC%s\n", i->seq, procname[i->proc]); else printf("%i YPPROC%i\n", i->seq, i->proc); switch(i->proc) { case 0: handle_procnull(s, i); return; case 1: case 2: case 11: decode_domainname(i); return; case 3: case 4: case 5: decode_ypreq_key(i); return; case 6: decode_ypreq_xfr(i); return; case 7: return; case 8: case 9: case 10: decode_ypreq_nokey(i); return; } } /* Handle YPPASSWDPROG - replies to nothing */ void decode_newpw(struct inst *i) { decode_string_variable(i, "pw_name", -1); decode_string_variable(i, "pw_passwd", -1); decode_int(i, "pw_uid"); decode_int(i, "pw_gid"); decode_string_variable(i, "pw_gecos", -1); decode_string_variable(i, "pw_dir", -1); decode_string_variable(i, "pw_shell", -1); } void decode_yppasswd(struct inst *i) { decode_string_variable(i, "oldpass", -1); decode_newpw(i); } void handle_yppasswd(struct serv *s, struct inst *i) { const char *procname[]={ "-", "UPDATE"}; s=s; if (i->proc>=1 && i->proc<=1) printf("%i YPPASSWDPROC_%s\n", i->seq, procname[i->proc]); else printf("%i YPPASSWDPROC%i\n", i->seq, i->proc); switch(i->proc) { case 1: decode_yppasswd(i); return; } } /* Handle YPUPD (YP Update daemon) - replies to nothing */ void decode_ypupdateargs(struct inst *i) { decode_string_variable(i, "mapname", 255); decode_opaque_variable(i, "key", 1023); decode_opaque_variable(i, "datum", 1023); } void decode_ypdeleteargs(struct inst *i) { decode_string_variable(i, "mapname", 255); decode_opaque_variable(i, "key", 1023); } void handle_ypu(struct serv *s, struct inst *i) { const char *procname[]={ "-", "CHANGE", "INSERT", "DELETE", "STORE"}; s=s; if (i->proc>=1 && i->proc<=4) printf("%i YPU_%s\n", i->seq, procname[i->proc]); else printf("%i YPUPROC%i\n", i->seq, i->proc); switch(i->proc) { case 1: case 2: case 4: decode_ypupdateargs(i); return; case 3: decode_ypdeleteargs(i); return; } } /* REX decoder - replies to nothing */ void decode_rexarray(struct inst *i, const char *name) { int *p=i->pos; if (p+1<=i->endp) { int args=ntohl(*p++); printf("%i %s: %i parameters\n", i->seq, name, args); i->pos=p; while(args--) decode_string_variable(i, name, 1024); } } void decode_rex_start(struct inst *i) { /* FIXME - decode unbounded array of variable arrays */ decode_rexarray(i, "cmd"); decode_string_variable(i, "rst_host", 1024); decode_string_variable(i, "rst_fsname", 1024); decode_string_variable(i, "rst_dirwithin", 1024); decode_rexarray(i, "env"); decode_uint(i, "rst_port0"); decode_uint(i, "rst_port1"); decode_uint(i, "rst_port2"); decode_uint(i, "rst_flags"); } void decode_rex_ttymode(struct inst *i) { decode_uint(i, "four"); decode_opaque_fixed(i, "sgttyb.chars", 4); decode_int(i, "flags"); } void decode_rex_ttysize(struct inst *i) { decode_int(i, "lines"); decode_int(i, "cols"); } void decode_rex_signal(struct inst *i) { decode_int(i, "signal"); } void handle_rex(struct serv *s, struct inst *i) { const char *procname[]={ "-", "START", "WAIT", "TTYMODES", "WINCH", "SIGNAL"}; s=s; if (i->proc>=1 && i->proc<=5) printf("%i REXPROC_%s\n", i->seq, procname[i->proc]); else printf("%i REXPROC_%i\n", i->seq, i->proc); switch(i->proc) { /* case 1: */ /* decode_rex_start(i); */ /* return; */ case 3: decode_rex_ttymode(i); return; case 4: decode_rex_ttysize(i); return; case 5: decode_rex_signal(i); return; } } /* MLN (Network Lock Manager) decoder - */ void decode_netobj(struct inst *i, const char *name) { decode_opaque_variable(i, name, 1024); } void decode_bool(struct inst *i, const char *name) { decode_int(i, name); } void decode_nlm_lock(struct inst *i) { decode_string_variable(i, "caller_name", 1024); decode_netobj(i, "fh"); decode_netobj(i, "oh"); decode_uint(i, "l_offset"); decode_uint(i, "l_len"); } void decode_nlm_testargs(struct inst *i) { decode_netobj(i, "cookie"); decode_bool(i, "exclusive"); decode_nlm_lock(i); } void decode_nlm_lockargs(struct inst *i) { decode_netobj(i, "cookie"); decode_bool(i, "lock"); decode_bool(i, "exclusive"); decode_nlm_lock(i); decode_bool(i, "reclaim"); decode_int(i, "state"); } void decode_nlm_unlockargs(struct inst *i) { decode_netobj(i, "cookie"); decode_nlm_lock(i); } void decode_nlm_cancargs(struct inst *i) { decode_netobj(i, "cookie"); decode_bool(i, "lock"); decode_bool(i, "exclusive"); decode_nlm_lock(i); } void decode_nlm_stat(struct inst *i) { const char *stats[]={ "granted", "denied", "denied_nolocks", "blocked", "denied_grace_period"}; int *p=i->pos; if (p+1<=i->endp) { int stat=ntohl(*p++); if (stat>=0 && stat<=4) printf("%i %si\n", i->seq, stats[stat]); else printf("%i invalid nlm_stats: %i\n", i->seq, stat); } i->pos=p; } void decode_fsh_mode(struct inst *i) { const char *modes[]={ "deny_none", "deny_read", "deny_write", "deny_read/write"}; int *p=i->pos; if (p+1<=i->endp) { int mode=ntohl(*p++); if (mode>=0 && mode<=3) printf("%i %si\n", i->seq, modes[mode]); else printf("%i invalid fsh_mode: %i\n", i->seq, mode); } i->pos=p; } void decode_fsh_access(struct inst *i) { const char *modes[]={ "none", "read_only", "write_only", "read/write"}; int *p=i->pos; if (p+1<=i->endp) { int mode=ntohl(*p++); if (mode>=0 && mode<=3) printf("%i %si\n", i->seq, modes[mode]); else printf("%i invalid fsh_access: %i\n", i->seq, mode); } i->pos=p; } void decode_nlm_testrply(struct inst *i) { int *p=i->pos; if (p+1<=i->endp) { decode_nlm_stat(i); if (ntohl(*p++)==1) { printf("%i denied\n", i->seq); decode_int(i, "exclusive"); decode_bool(i, "svid"); decode_netobj(i, "oh"); decode_uint(i, "l_offset"); decode_uint(i, "l_len"); } } i->pos=p; } void decode_nlm_testres(struct inst *i) { decode_netobj(i, "cookie"); decode_nlm_testrply(i); } void decode_nlm_res(struct inst *i) { decode_netobj(i, "cookie"); decode_nlm_stat(i); } void decode_nlm_share(struct inst *i) { decode_string_variable(i, "caller_name", 1024); decode_netobj(i, "fh"); decode_netobj(i, "oh"); decode_fsh_mode(i); decode_fsh_access(i); } void decode_nlm_shareargs(struct inst *i) { decode_netobj(i, "cookie"); decode_nlm_share(i); decode_bool(i, "reclaim"); } void decode_nlm_notify(struct inst *i) { decode_string_variable(i, "name", 1025); decode_int(i, "state"); } void handle_nlm(struct serv *s, struct inst *i) { const char *proc1name[]={ "-", "TEST", "LOCK", "CANCEL", "UNLOCK", "GRANTED", "TEST_MSG", "LOCK_MSG", "CANCEL_MSG", "UNLOCK_MSG", "GRANTED_MSG", "TEST_RES", "LOCK_RES", "CANCEL_RES", "UNLOCK_RES", "GRANTED_RES"}; const char *proc3name_20[]={ "SHARE", "UNSHARE", "NM_LOCK", "FREE_ALL"}; s=s; if (i->vers==1 && i->proc>=1 && i->proc<=15) printf("%i MOUNTPROC_%s\n", i->seq, proc1name[i->proc]); else if (i->vers==3 && i->proc>=20 && i->proc<23) printf("%i MOUNTPROC_%s\n", i->seq, proc3name_20[i->proc-20]); else printf("%i MOUNTPROC_%i\n", i->seq, i->proc); if (i->vers==1) switch(i->proc) { case 1: case 5: case 6: case 10: decode_nlm_testargs(i); return; case 2: case 7: decode_nlm_lockargs(i); return; case 3: case 8: decode_nlm_cancargs(i); return; case 4: case 9: decode_nlm_unlockargs(i); return; case 11: decode_nlm_testres(i); return; case 12: case 13: case 14: case 15: decode_nlm_res(i); return; } if (i->vers==3) switch(i->proc) { case 20: case 21: decode_nlm_shareargs(i); return; case 22: decode_nlm_lockargs(i); return; case 23: decode_nlm_notify(i); return; } } /* MOUNT decoder - replies to EXPORT and MNT */ void handle_mount(struct serv *s, struct inst *i) { const char *procname[]={ "NULL", "MNT", "DUMP", "UMNT", "UMNTALL", "EXPORT", "EXPORTALL"}; if (i->proc<7) printf("%i MOUNTPROC_%s\n", i->seq, procname[i->proc]); else printf("%i MOUNTPROC_%i\n", i->seq, i->proc); switch(i->proc) { case 0: handle_procnull(s, i); return; case 1: mount_mnt(i); return; case 3: decode_dirpath(i); return; case 5: case 6: mount_export(i); return; } } /* PMAP decoder - replies to GETPORT and DUMP */ void handle_pmap(struct serv *s, struct inst *i) { const char *procname[]={ "NULL", "SET", "UNSET", "GETPORT", "DUMP", "CALLIT"}; if (i->proc<6) printf("%i PMAPPROC_%s\n", i->seq, procname[i->proc]); else printf("%i PMAPPROC_%i\n", i->seq, i->proc); switch(i->proc) { case 0: handle_procnull(s, i); return; case 1: case 2: decode_pmap(i); return; case 3: pmap_getport(i); return; case 4: pmap_dump(i); return; case 5: decode_opaque_variable(i, "callit", -1); return; } } /* Handle SM_PROG (status monitor) - reply to nothing */ void decode_sm_name(struct inst *i) { decode_string_variable(i, "mon_name", 1024); } void decode_my_id(struct inst *i) { int *p; decode_string_variable(i, "my_name", 1024); p=i->pos; printf("%i", i->seq); if (p+1<=i->endp) printf(" my_prog=%i", ntohl(*p++)); if (p+1<=i->endp) printf(" my_vers=%i", ntohl(*p++)); if (p+1<=i->endp) printf(" my_proc=%i", ntohl(*p++)); printf("\n"); i->pos=p; } void decode_mon_id(struct inst *i) { decode_string_variable(i, "mon_name", 1024); decode_my_id(i); } void decode_mon(struct inst *i) { decode_mon_id(i); decode_opaque_fixed(i, "priv", 16); } void handle_sm(struct serv *s, struct inst *i) { const char *procname[]={ "-", "STAT", "MON", "UNMON", "UNMON_ALL", "SIMU_CRASH"}; s=s; if (i->proc>=1 && i->proc<=5) printf("%i SM_%s\n", i->seq, procname[i->proc]); else printf("%i SM_PROC%i\n", i->seq, i->proc); switch(i->proc) { case 1: decode_sm_name(i); return; case 2: decode_mon(i); return; case 3: decode_mon_id(i); return; case 4: decode_my_id(i); return; } } /* Handle RQUOTAPROG - reply to nothing */ void decode_getquota_args(struct inst *i) { decode_string_variable(i, "gqa_pathp", 1024); decode_int(i, "gqa_uid"); } void handle_rquota(struct serv *s, struct inst *i) { const char *procname[]={ "-", "GETQUOTA", "GETACTIVEQUOTA"}; s=s; if (i->proc>=1 && i->proc<=2) printf("%i RQUOTAPROC_%s\n", i->seq, procname[i->proc]); else printf("%i RQUOTAPROC_%i\n", i->seq, i->proc); switch(i->proc) { case 1: case 2: decode_getquota_args(i); return; } } /* Handle WALLPROG - reply to nothing */ void handle_wall(struct serv *s, struct inst *i) { const char *procname[]={ "-", "WALL"}; s=s; if (i->proc>=1 && i->proc<=1) printf("%i WALLPROC_WALL%s\n", i->seq, procname[i->proc]); else printf("%i WALLPROC_%i\n", i->seq, i->proc); switch(i->proc) { case 1: decode_string_variable(i, "wrapstring", -1); return; } } /* Handle BOOTPARAMPROG - reply to nothing */ void decode_bp_ipaddr(struct inst *i) { int *p=i->pos; printf("%i", i->seq); if (p+1<=i->endp) { unsigned char *cp=(char *)p; if (cp+1<=(unsigned char *)i->endp) printf(" net=%i", *cp++); if (cp+1<=(unsigned char *)i->endp) printf(" host=%i", *cp++); if (cp+1<=(unsigned char *)i->endp) printf(" lh=%i", *cp++); if (cp+1<=(unsigned char *)i->endp) printf(" impno=%i", *cp++); p++; } printf("\n"); i->pos=p; } void decode_bp_address(struct inst *i) { int *p=i->pos; if (p+1<=i->endp) { int address_type; switch((address_type=ntohl(*p++))) { case 1: i->pos=p; decode_bp_ipaddr(i); return; default: printf("%i Unknown bp_address type: %i\n", i->seq, address_type); } } i->pos=p; } void decode_bp_whoami(struct inst *i) { decode_bp_address(i); } void decode_bp_getfile(struct inst *i) { decode_string_variable(i, "client_name", 255); decode_string_variable(i, "file_id", 32); } void handle_bootparam(struct serv *s, struct inst *i) { const char *procname[]={ "-", "WHOAMI", "GETFILE"}; s=s; if (i->proc>=1 && i->proc<=2) printf("%i BOOTPARAMPROC_%s\n", i->seq, procname[i->proc]); else printf("%i BOOTPARAMPROC_%i\n", i->seq, i->proc); switch(i->proc) { case 1: decode_bp_whoami(i); return; case 2: decode_bp_getfile(i); return; } } /* Handle RSTATPROG - reply to nothing */ void handle_rstat(struct serv *s, struct inst *i) { const char *procname[]={ "-", "STATS", "HAVEDISK"}; s=s; if (i->proc>=1 && i->proc<=2) printf("%i RSTATPROC_%s\n", i->seq, procname[i->proc]); else printf("%i RSTATPROC_%i\n", i->seq, i->proc); } /* Handle RUSERSPROG - reply to nothing */ void handle_rusers(struct serv *s, struct inst *i) { const char *procname[]={ "-", "NUM", "NAMES", "ALLNAMES"}; s=s; if (i->proc>=1 && i->proc<=3) printf("%i RUSERSPROC_%s\n", i->seq, procname[i->proc]); else printf("%i RUSERSPROC_%i\n", i->seq, i->proc); } /* Handle SPRAYPROG - reply to nothing */ void decode_sprayarr(struct inst *i) { decode_opaque_variable(i, "sprayarr", 1024); } void handle_spray(struct serv *s, struct inst *i) { const char *procname[]={ "-", "SPRAY", ""}; s=s; if (i->proc>=1 && i->proc<=2) printf("%i SPRAYPROC_%s\n", i->seq, procname[i->proc]); else printf("%i SPRAYPROC_%i\n", i->seq, i->proc); switch(i->proc) { case 1: decode_sprayarr(i); return; } } /* doesn't update i->pos properly, but only used from one place where it is ignored */ void decode_authunix(struct inst *i) { int len; int *p=i->pos; if (p+1<=i->endp) printf("%i time=%i\n", i->seq, ntohl(*p++)); i->pos=p; decode_string_variable(i, "machine", 255); p=i->pos; printf("%i", i->seq); if (p+1<=i->endp) printf(" uid=%i", ntohl(*p++)); if (p+1<=i->endp) printf(" gid=%i", ntohl(*p++)); if (p+1<=i->endp) { len=ntohl(*p++); if (len>16) printf(" group_len exceeds 16\n"); printf(" group_len=%i <", len); while (p+1<=i->endp && len--) printf("%i%s", ntohl(*p++), len?",":""); printf(">"); } printf("\n"); dump(i->seq, p, (void *)(i->endp)-(void *)p); } /* Generic RPC decoder */ void handle_rpc(struct inst *i) { int authflav=0, authlen; int dir=0, rpcvers=0; struct serv *server; int *p=i->pos; printf("%i", i->seq); if (p+1<=i->endp) printf(" xid=%x", ntohl((i->xid)=*p++)); if (p+1<=i->endp) printf(" dir=%s", (dir=ntohl(*p++))?"Reply":"Call"); if (p+1<=i->endp) printf(" rpcvers=%i", rpcvers=ntohl(*p++)); if (rpcvers!=2) { printf("\n"); rpcverserror(i); return; } if (p+1<=i->endp) printf(" prog=%i", (i->prog)=ntohl(*p++)); if (p+1<=i->endp) printf(" vers=%i", (i->vers)=ntohl(*p++)); if (p+1<=i->endp) printf(" proc=%i\n", (i->proc)=ntohl(*p++)); printf("%i", i->seq); if (p+1<=i->endp) printf(" cred.flavour=%i", authflav=ntohl(*p++)); if (p+1<=i->endp) { printf(" cred.length=%i", authlen=ntohl(*p++)); if (authlen&3) { printf("\n"); swarn(i->seq, "cred.authlen was not a multiple of 4\n"); goto end; } if (authflav==1) { int *oldpos = i->pos; int *oldendp = i->endp; printf(" AUTH_UNIX\n"); i->pos = (int *)p; i->endp = (int *)(p+(authlen>>2)); decode_authunix(i); i->pos = oldpos; i->endp = oldendp; printf("%i", i->seq); } p+=authlen>>2; } if (p+1<=i->endp) printf(" verf.flavour=%i", ntohl(*p++)); if (p+1<=i->endp) { printf(" verf.length=%i", authlen=ntohl(*p++)); if (authlen&3) { printf("\n"); swarn(i->seq, "verf.authlen was not a multiple of 4\n"); goto end; } p+=authlen>>2; } printf("\n"); if (p>i->endp) return; if (dir) goto end; server = getservice(i->prog); i->pos = (int *)p; if (server->handler) { server->handler(server, i); /* call handler routine */ } else { handle_generic(server, i); /* use generic routine */ } p = i->pos; end: if (p!=i->endp) { int len=(void *)(i->endp)-(void *)p; printf("%i Trailing data (%i byte%s)\n", i->seq, len, (len==1)?"":"s"); dump(i->seq, p, len); } printf("%i End\n", i->seq); fflush(NULL); } /* Called to send tcp replies */ void puttcp(int fd, struct sockaddr *addr, int addrlen, void *buf, int len) { int mylen=htonl(len | 0x80000000); addr=addr; addrlen=addrlen; send(fd, &mylen, sizeof(mylen), 0); send(fd, buf, len, 0); } void alarmhandler(int sig) { sig=sig; } /* Called to process tcp connections */ void gotrpc_tcp(int seq, int sock) { int s; struct sockaddr_in from, to; int fromlen=sizeof(from), tolen=sizeof(to); int bytes; char fromstr[22], tostr[22]; pid_t pid; time_t now; struct inst instance; s=accept(sock, (struct sockaddr *)&from, &fromlen); if (s==-1) { swarn(seq, "recvfrom: %s\n", strerror(errno)); return; } if (fromlen!=sizeof(from)) { swarn(seq, "Source inet address length wrong " "(expected %i, got %i)\n", sizeof(from), fromlen); return; } if (getsockname(s, (struct sockaddr *)&to, &tolen)) { swarn(seq, "getsockname: %s\n", strerror(errno)); return; } if (fromlen!=sizeof(from)) { swarn(seq, "Destination inet address length wrong " "(expected %i, got %i)\n", sizeof(from), fromlen); return; } printf("%i Accepted connection from %s to %s\n", seq, inaddr(fromstr, &from), inaddr(tostr, &to)); now=time(NULL); printf("%i Time=%li %s", seq, (long)now, asctime(localtime(&now))); fflush(NULL); /* Flush output streams before fork */ if ((pid=fork())==(pid_t)-1) { swarn(seq, "fork: %s\n", strerror(errno)); close(s); return; } if (!pid) { char buf[RPC_MAXLEN]; int len; struct sigaction alarmact; struct itimerval itimer={{1, 0}, {RPCTCP_TIMEOUT,0}}; alarmact.sa_handler=alarmhandler; sigemptyset(&alarmact.sa_mask); alarmact.sa_flags=0; sigaction(SIGALRM, &alarmact, NULL); setitimer(ITIMER_REAL, &itimer, NULL); if ((bytes=read(s, buf, 4))==-1) { if (errno==EINTR) sdie(seq, "Timeout\n"); sdie(seq, "read: %s\n", strerror(errno)); } if (bytes!=4) sdie(seq, "read: Expected 4 bytes, got %i\n", bytes); len=ntohl(*(int *)buf) & 0x7FFFFFFF; if (len>RPC_MAXLEN) len=RPC_MAXLEN; if ((bytes=read(s, buf, len))==-1) { if (errno==EINTR) sdie(seq, "Timeout\n"); sdie(seq, "read: %s\n", strerror(errno)); } if (bytes!=len) sdie(seq, "read: Expected %i bytes, got %i\n", len, bytes); instance.seq = seq; instance.pos = (int *)buf; instance.endp = (int *)(buf+len); instance.sendfunc =puttcp; instance.fd = s; instance.addr = NULL; instance.addrlen =0; handle_rpc(&instance); _exit(0); } close(s); } /* Called to send udp replies */ void putudp(int fd, struct sockaddr *addr, int addrlen, void *buf, int len) { sendto(fd, buf, len, 0, addr, addrlen); } /* Called to process udp packets */ void gotrpc_udp(int seq, int s) { char buf[RPC_MAXLEN]; struct sockaddr_in from; int fromlen=sizeof(from); int bytes; char fromstr[22]; time_t now; struct inst instance; bytes=recvfrom(s, buf, RPC_MAXLEN, 0, (struct sockaddr *)&from, &fromlen); if (bytes==-1) { swarn(seq, "recvfrom: %s\n", strerror(errno)); return; } if (fromlen!=sizeof(from)) { swarn(seq, "Source inet address length wrong " "(expected %i, got %i)\n", sizeof(from), fromlen); return; } if (fromlen!=sizeof(from)) { swarn(seq, "Destination inet address length wrong " "(expected %i, got %i)\n", sizeof(from), fromlen); return; } printf("%i Received packet from %s\n", seq, inaddr(fromstr, &from)); now=time(NULL); printf("%i Time=%li %s", seq, (long)now, asctime(localtime(&now))); instance.seq = seq; instance.pos = (int *)buf; instance.endp = (int *)(buf+bytes); instance.sendfunc = putudp; instance.fd = s; instance.addr =(struct sockaddr *)&from; instance.addrlen = fromlen; handle_rpc(&instance); } void warn(const char *msg, ...) { va_list al; va_start(al, msg); fprintf(stderr, "%s: ", progname); vfprintf(stderr, msg, al); va_end(al); } void die(const char *msg, ...) { va_list al; va_start(al, msg); fprintf(stderr, "%s: ", progname); vfprintf(stderr, msg, al); va_end(al); exit(1); } void enomem(void) { die("%s\n", strerror(ENOMEM)); exit(1); } void construct_pmapdump_reply(void) { int i,j; struct pmap_item { int item, prog, vers, prot, port; } *pmap_entry; struct pmap_reply_struct { int xid, dir, replystat, verftype, verflen, acceptstat; } *pmap_header; for (i=0,j=0; pmap_serv_table[i].prog; i++) j+=2*(1+pmap_serv_table[i].high-pmap_serv_table[i].low); pmap_dump_reply_len=sizeof(struct pmap_reply_struct)+ j*sizeof(struct pmap_item)+sizeof(int); if (!(pmap_dump_reply=malloc(pmap_dump_reply_len))) enomem(); pmap_header=pmap_dump_reply; pmap_entry=(struct pmap_item *)(pmap_header+1); pmap_header->dir=htonl(1); pmap_header->replystat=pmap_header->verftype=pmap_header->verflen= pmap_header->acceptstat=0; for (i=0; pmap_serv_table[i].prog; i++) { int proto[2]={6,17}, k; unsigned int prog=pmap_serv_table[i].prog; int high=pmap_serv_table[i].high; for (j=pmap_serv_table[i].low; j<=high; j++) for (k=0; k<2; k++){ pmap_entry->item=1; pmap_entry->prog=htonl(prog); pmap_entry->vers=htonl(j); pmap_entry->prot=htonl(proto[k]); pmap_entry->port=hport; pmap_entry++; } } *((int *)pmap_entry)=0; } void reap(void) { pid_t pid; while ((pid=waitpid(-1, NULL, WNOHANG))) { if (pid==(pid_t)-1) { if (errno==ECHILD) return; warn("waitpid: %s\n", strerror(errno)); return; } } } void chldhandler(int sig) { sig=sig; reap(); } void startreaper(void) { struct sigaction chldact; chldact.sa_handler=chldhandler; sigemptyset(&chldact.sa_mask); chldact.sa_flags=0; sigaction(SIGCHLD, &chldact, NULL); reap(); } struct serv pmap_serv_table[]={ {1, 100000,2,2, "PMAPPROG", handle_pmap}, {0, 100001,1,3, "RSTATPROG", handle_rstat}, {0, 100002,3,3, "RUSERSPROG", handle_rusers}, {0, 100003,2,3, "NFS_PROGRAM", NULL}, {1, 100004,2,2, "YPPROG", handle_yp}, {1, 100005,1,1, "MOUNTPROG", handle_mount}, {1, 100007,2,2, "YPBINDPROG", handle_ypbind}, {0, 100008,1,1, "WALLPROG", handle_wall}, {0, 100009,1,1, "YPPASSWD", handle_yppasswd}, {0, 100011,2,3, "RQUOTAPROG", handle_rquota}, {0, 100012,2,2, "SPRAYPROG", handle_spray}, {0, 100017,1,1, "REXPROG", handle_rex}, {0, 100021,1,4, "NLM_PROG", handle_nlm}, {0, 100023,1,4, "statmon", NULL}, {0, 100024,1,1, "SM_PROG", handle_sm}, {0, 100026,1,1, "bootparam", handle_bootparam}, {0, 100028,1,1, "ypupdated", handle_ypu}, {0, 100029,2,2, "KEY_PROG", NULL}, {0, 100069,1,1, "ypxfr", NULL}, {0, 100083,1,4, "ttdbserverd", NULL}, {0, 100099,2,2, "AUTOFS_PROG", NULL}, {0, 100227,2,3, "NFS_ACL_PROGRAM", NULL}, {0, 100231,1,1, "NFSAUTH_PROG", NULL}, {0, 100232,2,3, "sadmind", NULL}, {0, 150001,1,1, "pcnfsd", NULL}, {0, 300019,1,1, "amd", NULL}, {0, 391000,1,3, "sgi_snoopd", NULL}, {0, 391002,1,3, "sgi_sgi_fam", NULL}, {0, 391006,1,3, "sgi_pcsd", NULL}, {0, 391016,1,3, "sgi_xfsmd", NULL}, {0, 391017,1,3, "sgi_mediad", NULL}, {0, 0,1,8, "", NULL} }; int main(int argc, char *argv[]) { struct sockaddr_in addr; int s1, s2, s3, s4, maxfd=-1; int sequence=0; if (argc) setprogname(argv[0]); addr.sin_family=AF_INET; addr.sin_addr.s_addr=INADDR_ANY; /* Bind to portmapper port */ addr.sin_port=hport=htons(pmapport); if ((s1=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))==-1) die("socket: %s\n", strerror(errno)); if (bind(s1, (struct sockaddr *)&addr, sizeof(addr))) die("bind: %s\n", strerror(errno)); if ((s2=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) die("socket: %s\n", strerror(errno)); if (bind(s2, (struct sockaddr *)&addr, sizeof(addr))) die("bind: %s\n", strerror(errno)); /* Demote privilege */ { struct passwd *pwd; uid_t uid; /* user id */ gid_t gid; /* group id */ errno=0; /* in case getpw* doesn't set it */ pwd = getpwnam(rpcduser); if (pwd == NULL) { if (errno != 0) die("getpwnam(%s): %s\n", rpcduser, strerror(errno)); die("getpwnam(%s): Failed to lookup entry in password map\n", rpcduser); }; uid = pwd->pw_uid; gid = pwd->pw_gid; if (initgroups(rpcduser, pwd->pw_gid)) die("initgroups: %s\n", strerror(errno)); if (setgid(gid)) die("setgid(%d): %s\n", gid, strerror(errno)); if (setuid(uid)) die("setuid(%d): %s\n", uid, strerror(errno)); }; /* Bind to other port */ addr.sin_port=hport=htons(localport); if ((s3=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))==-1) die("socket: %s\n", strerror(errno)); if (bind(s3, (struct sockaddr *)&addr, sizeof(addr))) die("bind: %s\n", strerror(errno)); if ((s4=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) die("socket: %s\n", strerror(errno)); if (bind(s4, (struct sockaddr *)&addr, sizeof(addr))) die("bind: %s\n", strerror(errno)); /* Set TCP socket to non-blocking to avoid blocking inside accept */ if (fcntl(s1, F_SETFL, O_NONBLOCK)) die("fcntl: %s\n", strerror(errno)); if (fcntl(s3, F_SETFL, O_NONBLOCK)) die("fcntl: %s\n", strerror(errno)); if (listen(s1, 5)) die("listen: %s\n", strerror(errno)); if (listen(s3, 5)) die("listen: %s\n", strerror(errno)); /* Initialise variables used for constructing pmap replies */ hport=htonl(localport); construct_pmapdump_reply(); startreaper(); /* Wait for children */ setlinebuf(stdout); /* Try to force output immediately */ /* Main loop */ maxfd=s1>maxfd? s1:maxfd; maxfd=s2>maxfd? s2:maxfd; maxfd=s3>maxfd? s3:maxfd; maxfd=s4>maxfd? s4:maxfd; while(1) { fd_set rfds; FD_ZERO(&rfds); FD_SET(s1, &rfds); FD_SET(s2, &rfds); FD_SET(s3, &rfds); FD_SET(s4, &rfds); while (select(maxfd+1, &rfds, NULL, NULL, NULL)==-1) { if (errno==EINTR) continue; die("select: %s\n", strerror(errno)); } if (FD_ISSET(s1, &rfds)) gotrpc_tcp(sequence++, s1); if (FD_ISSET(s2, &rfds)) gotrpc_udp(sequence++, s2); if (FD_ISSET(s3, &rfds)) gotrpc_tcp(sequence++, s3); if (FD_ISSET(s4, &rfds)) gotrpc_udp(sequence++, s4); } }