Index: stable/10/contrib/smbfs/lib/smb/ctx.c =================================================================== --- stable/10/contrib/smbfs/lib/smb/ctx.c (revision 335780) +++ stable/10/contrib/smbfs/lib/smb/ctx.c (revision 335781) @@ -1,775 +1,781 @@ /* * Copyright (c) 2000-2002, Boris Popov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ctx.c,v 1.24 2002/04/13 14:35:28 bp Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NB_NEEDRESOLVER #include #include #include #include #include /* * Prescan command line for [-U user] argument * and fill context with defaults */ int smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[], int minlevel, int maxlevel, int sharetype) { int opt, error = 0; uid_t euid; const char *arg, *cp; struct passwd *pwd; bzero(ctx,sizeof(*ctx)); error = nb_ctx_create(&ctx->ct_nb); if (error) return error; ctx->ct_fd = -1; ctx->ct_parsedlevel = SMBL_NONE; ctx->ct_minlevel = minlevel; ctx->ct_maxlevel = maxlevel; ctx->ct_smbtcpport = SMB_TCP_PORT; ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE; ctx->ct_ssn.ioc_timeout = 15; ctx->ct_ssn.ioc_retrycount = 4; ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER; ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP; ctx->ct_ssn.ioc_mode = SMBM_EXEC; ctx->ct_ssn.ioc_rights = SMBM_DEFAULT; ctx->ct_sh.ioc_opt = SMBVOPT_CREATE; ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER; ctx->ct_sh.ioc_group = SMBM_ANY_GROUP; ctx->ct_sh.ioc_mode = SMBM_EXEC; ctx->ct_sh.ioc_rights = SMBM_DEFAULT; ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER; ctx->ct_sh.ioc_group = SMBM_ANY_GROUP; nb_ctx_setscope(ctx->ct_nb, ""); euid = geteuid(); if ((pwd = getpwuid(euid)) != NULL) { smb_ctx_setuser(ctx, pwd->pw_name); endpwent(); } else if (euid == 0) smb_ctx_setuser(ctx, "root"); else return 0; if (argv == NULL) return 0; for (opt = 1; opt < argc; opt++) { cp = argv[opt]; if (strncmp(cp, "//", 2) != 0) continue; error = smb_ctx_parseunc(ctx, cp, sharetype, (const char**)&cp); if (error) return error; ctx->ct_uncnext = cp; break; } while (error == 0 && (opt = cf_getopt(argc, argv, ":E:L:U:")) != -1) { arg = cf_optarg; switch (opt) { case 'E': error = smb_ctx_setcharset(ctx, arg); if (error) return error; break; case 'L': error = nls_setlocale(arg); if (error) break; break; case 'U': error = smb_ctx_setuser(ctx, arg); break; } } cf_optind = cf_optreset = 1; return error; } void smb_ctx_done(struct smb_ctx *ctx) { if (ctx->ct_ssn.ioc_server) nb_snbfree(ctx->ct_ssn.ioc_server); if (ctx->ct_ssn.ioc_local) nb_snbfree(ctx->ct_ssn.ioc_local); if (ctx->ct_srvaddr) free(ctx->ct_srvaddr); if (ctx->ct_nb) nb_ctx_done(ctx->ct_nb); } static int getsubstring(const char *p, u_char sep, char *dest, int maxlen, const char **next) { int len; maxlen--; for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) { if (*p == 0) return EINVAL; *dest = *p; } *dest = 0; *next = *p ? p + 1 : p; return 0; } /* * Here we expect something like "[proto:]//[user@]host[:psmb[:pnb]][/share][/path]" */ int smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype, const char **next) { const char *p = unc; char *p1, *psmb, *pnb; char tmp[1024]; int error ; ctx->ct_parsedlevel = SMBL_NONE; if (*p++ != '/' || *p++ != '/') { smb_error("UNC should start with '//'", 0); return EINVAL; } p1 = tmp; error = getsubstring(p, '@', p1, sizeof(tmp), &p); if (!error) { if (ctx->ct_maxlevel < SMBL_VC) { smb_error("no user name required", 0); return EINVAL; } error = smb_ctx_setuser(ctx, tmp); if (error) return error; ctx->ct_parsedlevel = SMBL_VC; } error = getsubstring(p, '/', p1, sizeof(tmp), &p); if (error) { error = getsubstring(p, '\0', p1, sizeof(tmp), &p); if (error) { smb_error("no server name found", 0); return error; } } if (*p1 == 0) { smb_error("empty server name", 0); return EINVAL; } /* * Check for port number specification. */ psmb = strchr(tmp, ':'); if (psmb) { *psmb++ = '\0'; pnb = strchr(psmb, ':'); if (pnb) { *pnb++ = '\0'; error = smb_ctx_setnbport(ctx, atoi(pnb)); if (error) { smb_error("Invalid NetBIOS port number", 0); return error; } } error = smb_ctx_setsmbport(ctx, atoi(psmb)); if (error) { smb_error("Invalid SMB port number", 0); return error; } } error = smb_ctx_setserver(ctx, tmp); if (error) return error; if (sharetype == SMB_ST_NONE) { *next = p; return 0; } if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) { smb_error("no share name required", 0); return EINVAL; } error = getsubstring(p, '/', p1, sizeof(tmp), &p); if (error) { error = getsubstring(p, '\0', p1, sizeof(tmp), &p); if (error) { smb_error("unexpected end of line", 0); return error; } } if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE) { smb_error("empty share name", 0); return EINVAL; } *next = p; if (*p1 == 0) return 0; error = smb_ctx_setshare(ctx, p1, sharetype); return error; } int smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg) { char *cp, *servercs, *localcs; int cslen = sizeof(ctx->ct_ssn.ioc_localcs); int scslen, lcslen, error; cp = strchr(arg, ':'); lcslen = cp ? (cp - arg) : 0; if (lcslen == 0 || lcslen >= cslen) { smb_error("invalid local charset specification (%s)", 0, arg); return EINVAL; } scslen = (size_t)strlen(++cp); if (scslen == 0 || scslen >= cslen) { smb_error("invalid server charset specification (%s)", 0, arg); return EINVAL; } localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen); localcs[lcslen] = 0; servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp); error = nls_setrecode(localcs, servercs); if (error == 0) return 0; smb_error("can't initialize iconv support (%s:%s)", error, localcs, servercs); localcs[0] = 0; servercs[0] = 0; return error; } int smb_ctx_setserver(struct smb_ctx *ctx, const char *name) { if (strlen(name) > SMB_MAXSRVNAMELEN) { smb_error("server name '%s' too long", 0, name); return ENAMETOOLONG; } nls_str_upper(ctx->ct_ssn.ioc_srvname, name); return 0; } int smb_ctx_setnbport(struct smb_ctx *ctx, int port) { if (port < 1 || port > 0xffff) return EINVAL; ctx->ct_nb->nb_nmbtcpport = port; return 0; } int smb_ctx_setsmbport(struct smb_ctx *ctx, int port) { if (port < 1 || port > 0xffff) return EINVAL; ctx->ct_smbtcpport = port; ctx->ct_nb->nb_smbtcpport = port; return 0; } int smb_ctx_setuser(struct smb_ctx *ctx, const char *name) { if (strlen(name) > SMB_MAXUSERNAMELEN) { smb_error("user name '%s' too long", 0, name); return ENAMETOOLONG; } nls_str_upper(ctx->ct_ssn.ioc_user, name); return 0; } int smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name) { if (strlen(name) > SMB_MAXUSERNAMELEN) { smb_error("workgroup name '%s' too long", 0, name); return ENAMETOOLONG; } nls_str_upper(ctx->ct_ssn.ioc_workgroup, name); return 0; } int smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd) { if (passwd == NULL) return EINVAL; if (strlen(passwd) > SMB_MAXPASSWORDLEN) { smb_error("password too long", 0); return ENAMETOOLONG; } if (strncmp(passwd, "$$1", 3) == 0) smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd); else strcpy(ctx->ct_ssn.ioc_password, passwd); strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password); return 0; } int smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype) { if (strlen(share) > SMB_MAXSHARENAMELEN) { smb_error("share name '%s' too long", 0, share); return ENAMETOOLONG; } nls_str_upper(ctx->ct_sh.ioc_share, share); if (share[0] != 0) ctx->ct_parsedlevel = SMBL_SHARE; ctx->ct_sh.ioc_stype = stype; return 0; } int smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr) { if (addr == NULL || addr[0] == 0) return EINVAL; if (ctx->ct_srvaddr) free(ctx->ct_srvaddr); if ((ctx->ct_srvaddr = strdup(addr)) == NULL) return ENOMEM; return 0; } static int smb_parse_owner(char *pair, uid_t *uid, gid_t *gid) { struct group *gr; struct passwd *pw; char *cp; cp = strchr(pair, ':'); if (cp) { *cp++ = '\0'; if (*cp) { gr = getgrnam(cp); if (gr) { *gid = gr->gr_gid; } else smb_error("Invalid group name %s, ignored", 0, cp); } } if (*pair) { pw = getpwnam(pair); if (pw) { *uid = pw->pw_uid; } else smb_error("Invalid user name %s, ignored", 0, pair); } endpwent(); return 0; } int smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg) { int error = 0; char *p, *cp; switch(opt) { case 'U': break; case 'I': error = smb_ctx_setsrvaddr(ctx, arg); break; case 'M': ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8); if (*cp == '/') { ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8); ctx->ct_flags |= SMBCF_SRIGHTS; } break; case 'N': ctx->ct_flags |= SMBCF_NOPWD; break; case 'O': p = strdup(arg); cp = strchr(p, '/'); if (cp) { *cp++ = '\0'; error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner, &ctx->ct_sh.ioc_group); } if (*p && error == 0) { error = smb_parse_owner(p, &ctx->ct_ssn.ioc_owner, &ctx->ct_ssn.ioc_group); } free(p); break; case 'P': /* ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT;*/ break; case 'R': ctx->ct_ssn.ioc_retrycount = atoi(arg); break; case 'T': ctx->ct_ssn.ioc_timeout = atoi(arg); break; case 'W': error = smb_ctx_setworkgroup(ctx, arg); break; } return error; } #if 0 static void smb_hexdump(const u_char *buf, int len) { int ofs = 0; while (len--) { if (ofs % 16 == 0) printf("\n%02X: ", ofs); printf("%02x ", *buf++); ofs++; } printf("\n"); } #endif static int smb_addiconvtbl(const char *to, const char *from, const u_char *tbl) { int error; error = kiconv_add_xlat_table(to, from, tbl); if (error && error != EEXIST) { smb_error("can not setup kernel iconv table (%s:%s)", error, from, to); return error; } return 0; } /* * Verify context before connect operation(s), * lookup specified server and try to fill all forgotten fields. */ int smb_ctx_resolve(struct smb_ctx *ctx) { struct smbioc_ossn *ssn = &ctx->ct_ssn; struct smbioc_oshare *sh = &ctx->ct_sh; struct nb_name nn; struct sockaddr *sap; struct sockaddr_nb *salocal, *saserver; char *cp; int error = 0; ctx->ct_flags &= ~SMBCF_RESOLVED; if (ssn->ioc_srvname[0] == 0) { smb_error("no server name specified", 0); return EINVAL; } if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0) { smb_error("no share name specified for %s@%s", 0, ssn->ioc_user, ssn->ioc_srvname); return EINVAL; } error = nb_ctx_resolve(ctx->ct_nb); if (error) return error; if (ssn->ioc_localcs[0] == 0) strcpy(ssn->ioc_localcs, "ISO8859-1"); error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower); if (error) return error; error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper); if (error) return error; if (ssn->ioc_servercs[0] != 0) { error = kiconv_add_xlat16_cspairs (ssn->ioc_servercs, ssn->ioc_localcs); if (error) return error; } if (ctx->ct_srvaddr) { error = nb_resolvehost_in(ctx->ct_srvaddr, &sap, ctx->ct_smbtcpport); } else { error = nbns_resolvename(ssn->ioc_srvname, ctx->ct_nb, &sap); } if (error) { smb_error("can't get server address", error); return error; } nn.nn_scope = ctx->ct_nb->nb_scope; nn.nn_type = NBT_SERVER; - strcpy(nn.nn_name, ssn->ioc_srvname); + if (strlen(ssn->ioc_srvname) > NB_NAMELEN) + return NBERROR(NBERR_NAMETOOLONG); + strlcpy(nn.nn_name, ssn->ioc_srvname, sizeof(nn.nn_name)); error = nb_sockaddr(sap, &nn, &saserver); nb_snbfree(sap); if (error) { smb_error("can't allocate server address", error); return error; } ssn->ioc_server = (struct sockaddr*)saserver; if (ctx->ct_locname[0] == 0) { error = nb_getlocalname(ctx->ct_locname); if (error) { smb_error("can't get local name", error); return error; } nls_str_upper(ctx->ct_locname, ctx->ct_locname); } - strcpy(nn.nn_name, ctx->ct_locname); + /* + * Truncate the local host name to NB_NAMELEN-1 which gives a + * suffix of 0 which is "workstation name". + */ + strlcpy(nn.nn_name, ctx->ct_locname, NB_NAMELEN); nn.nn_type = NBT_WKSTA; nn.nn_scope = ctx->ct_nb->nb_scope; error = nb_sockaddr(NULL, &nn, &salocal); if (error) { nb_snbfree((struct sockaddr*)saserver); smb_error("can't allocate local address", error); return error; } ssn->ioc_local = (struct sockaddr*)salocal; ssn->ioc_lolen = salocal->snb_len; ssn->ioc_svlen = saserver->snb_len; if (ssn->ioc_password[0] == 0 && (ctx->ct_flags & SMBCF_NOPWD) == 0) { cp = getpass("Password:"); error = smb_ctx_setpassword(ctx, cp); if (error) return error; } ctx->ct_flags |= SMBCF_RESOLVED; return 0; } static int smb_ctx_gethandle(struct smb_ctx *ctx) { int fd, i; char buf[20]; fd = open("/dev/"NSMB_NAME, O_RDWR); if (fd >= 0) { ctx->ct_fd = fd; return 0; } return ENOENT; } int smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags) { struct smbioc_lookup rq; int error; if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) { smb_error("smb_ctx_lookup() data is not resolved", 0); return EINVAL; } if (ctx->ct_fd != -1) { close(ctx->ct_fd); ctx->ct_fd = -1; } error = smb_ctx_gethandle(ctx); if (error) { smb_error("can't get handle to requester (no /dev/"NSMB_NAME"* device)", 0); return EINVAL; } bzero(&rq, sizeof(rq)); bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof(struct smbioc_ossn)); bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof(struct smbioc_oshare)); rq.ioc_flags = flags; rq.ioc_level = level; if (ioctl(ctx->ct_fd, SMBIOC_LOOKUP, &rq) == -1) { error = errno; if (flags & SMBLK_CREATE) smb_error("unable to open connection", error); return error; } return 0; } int smb_ctx_login(struct smb_ctx *ctx) { struct smbioc_ossn *ssn = &ctx->ct_ssn; struct smbioc_oshare *sh = &ctx->ct_sh; int error; if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) { smb_error("smb_ctx_resolve() should be called first", 0); return EINVAL; } if (ctx->ct_fd != -1) { close(ctx->ct_fd); ctx->ct_fd = -1; } error = smb_ctx_gethandle(ctx); if (error) { smb_error("can't get handle to requester", 0); return EINVAL; } if (ioctl(ctx->ct_fd, SMBIOC_OPENSESSION, ssn) == -1) { error = errno; smb_error("can't open session to server %s", error, ssn->ioc_srvname); return error; } if (sh->ioc_share[0] == 0) return 0; if (ioctl(ctx->ct_fd, SMBIOC_OPENSHARE, sh) == -1) { error = errno; smb_error("can't connect to share //%s/%s", error, ssn->ioc_srvname, sh->ioc_share); return error; } return 0; } int smb_ctx_setflags(struct smb_ctx *ctx, int level, int mask, int flags) { struct smbioc_flags fl; if (ctx->ct_fd == -1) return EINVAL; fl.ioc_level = level; fl.ioc_mask = mask; fl.ioc_flags = flags; if (ioctl(ctx->ct_fd, SMBIOC_SETFLAGS, &fl) == -1) return errno; return 0; } /* * level values: * 0 - default * 1 - server * 2 - server:user * 3 - server:user:share */ static int smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level) { char *p; int error; if (level >= 0) { rc_getstringptr(smb_rc, sname, "charsets", &p); if (p) { error = smb_ctx_setcharset(ctx, p); if (error) smb_error("charset specification in the section '%s' ignored", error, sname); } } if (level <= 1) { rc_getint(smb_rc, sname, "timeout", &ctx->ct_ssn.ioc_timeout); rc_getint(smb_rc, sname, "retry_count", &ctx->ct_ssn.ioc_retrycount); } if (level == 1) { rc_getstringptr(smb_rc, sname, "addr", &p); if (p) { error = smb_ctx_setsrvaddr(ctx, p); if (error) { smb_error("invalid address specified in the section %s", 0, sname); return error; } } } if (level >= 2) { rc_getstringptr(smb_rc, sname, "password", &p); if (p) smb_ctx_setpassword(ctx, p); } rc_getstringptr(smb_rc, sname, "workgroup", &p); if (p) smb_ctx_setworkgroup(ctx, p); return 0; } /* * read rc file as follows: * 1. read [default] section * 2. override with [server] section * 3. override with [server:user:share] section * Since abcence of rcfile is not fatal, silently ignore this fact. * smb_rc file should be closed by caller. */ int smb_ctx_readrc(struct smb_ctx *ctx) { char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN + SMB_MAXSHARENAMELEN + 4]; /* char *p;*/ if (smb_open_rcfile() != 0) return 0; if (ctx->ct_ssn.ioc_user[0] == 0 || ctx->ct_ssn.ioc_srvname[0] == 0) return 0; smb_ctx_readrcsection(ctx, "default", 0); nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0); smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1); nb_ctx_readrcsection(smb_rc, ctx->ct_nb, ctx->ct_ssn.ioc_srvname, 1); /* * SERVER:USER parameters */ snprintf(sname, sizeof(sname), "%s:%s", ctx->ct_ssn.ioc_srvname, ctx->ct_ssn.ioc_user); smb_ctx_readrcsection(ctx, sname, 2); if (ctx->ct_sh.ioc_share[0] != 0) { /* * SERVER:USER:SHARE parameters */ snprintf(sname, sizeof(sname), "%s:%s:%s", ctx->ct_ssn.ioc_srvname, ctx->ct_ssn.ioc_user, ctx->ct_sh.ioc_share); smb_ctx_readrcsection(ctx, sname, 3); } return 0; } Index: stable/10/contrib/smbfs/lib/smb/nbns_rq.c =================================================================== --- stable/10/contrib/smbfs/lib/smb/nbns_rq.c (revision 335780) +++ stable/10/contrib/smbfs/lib/smb/nbns_rq.c (revision 335781) @@ -1,381 +1,381 @@ /* * Copyright (c) 2000, Boris Popov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: nbns_rq.c,v 1.5 2001/02/17 03:07:24 bp Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #define NB_NEEDRESOLVER #include #include #include static int nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp); static void nbns_rq_done(struct nbns_rq *rqp); static int nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp); static int nbns_rq_prepare(struct nbns_rq *rqp); static int nbns_rq(struct nbns_rq *rqp); static struct nb_ifdesc *nb_iflist; int nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp) { struct nbns_rq *rqp; struct nb_name nn; struct nbns_rr rr; struct sockaddr_in *dest; int error, rdrcount, len; if (strlen(name) > NB_NAMELEN) return NBERROR(NBERR_NAMETOOLONG); error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp); if (error) return error; bzero(&nn, sizeof(nn)); - strcpy(nn.nn_name, name); + strlcpy(nn.nn_name, name, sizeof(nn.nn_name)); nn.nn_scope = ctx->nb_scope; nn.nn_type = NBT_SERVER; rqp->nr_nmflags = NBNS_NMFLAG_RD; rqp->nr_qdname = &nn; rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB; rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN; rqp->nr_qdcount = 1; dest = &rqp->nr_dest; *dest = ctx->nb_ns; dest->sin_family = AF_INET; dest->sin_len = sizeof(*dest); if (dest->sin_port == 0) dest->sin_port = htons(ctx->nb_nmbtcpport); if (dest->sin_addr.s_addr == INADDR_ANY) dest->sin_addr.s_addr = htonl(INADDR_BROADCAST); if (dest->sin_addr.s_addr == INADDR_BROADCAST) rqp->nr_flags |= NBRQF_BROADCAST; error = nbns_rq_prepare(rqp); if (error) { nbns_rq_done(rqp); return error; } rdrcount = NBNS_MAXREDIRECTS; for (;;) { error = nbns_rq(rqp); if (error) break; if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) { if (rdrcount-- == 0) { error = NBERROR(NBERR_TOOMANYREDIRECTS); break; } error = nbns_rq_getrr(rqp, &rr); if (error) break; error = nbns_rq_getrr(rqp, &rr); if (error) break; bcopy(rr.rr_data, &dest->sin_addr, 4); rqp->nr_flags &= ~NBRQF_BROADCAST; continue; } if (rqp->nr_rpancount == 0) { error = NBERROR(NBERR_HOSTNOTFOUND); break; } error = nbns_rq_getrr(rqp, &rr); if (error) break; len = sizeof(struct sockaddr_in); dest = malloc(len); if (dest == NULL) return ENOMEM; bzero(dest, len); dest->sin_len = len; dest->sin_family = AF_INET; bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4); dest->sin_port = htons(ctx->nb_smbtcpport); *adpp = (struct sockaddr*)dest; ctx->nb_lastns = rqp->nr_sender; break; } nbns_rq_done(rqp); return error; } int nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp) { struct nbns_rq *rqp; static u_int16_t trnid; int error; rqp = malloc(sizeof(*rqp)); if (rqp == NULL) return ENOMEM; bzero(rqp, sizeof(*rqp)); error = mb_init(&rqp->nr_rq, NBDG_MAXSIZE); if (error) { free(rqp); return error; } rqp->nr_opcode = opcode; rqp->nr_nbd = ctx; rqp->nr_trnid = trnid++; *rqpp = rqp; return 0; } void nbns_rq_done(struct nbns_rq *rqp) { if (rqp == NULL) return; if (rqp->nr_fd >= 0) close(rqp->nr_fd); mb_done(&rqp->nr_rq); mb_done(&rqp->nr_rp); free(rqp); } /* * Extract resource record from the packet. Assume that there is only * one mbuf. */ int nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp) { struct mbdata *mbp = &rqp->nr_rp; u_char *cp; int error, len; bzero(rrp, sizeof(*rrp)); cp = mbp->mb_pos; len = nb_encname_len(cp); if (len < 1) return NBERROR(NBERR_INVALIDRESPONSE); rrp->rr_name = cp; error = mb_get_mem(mbp, NULL, len); if (error) return error; mb_get_uint16be(mbp, &rrp->rr_type); mb_get_uint16be(mbp, &rrp->rr_class); mb_get_uint32be(mbp, &rrp->rr_ttl); mb_get_uint16be(mbp, &rrp->rr_rdlength); rrp->rr_data = mbp->mb_pos; error = mb_get_mem(mbp, NULL, rrp->rr_rdlength); return error; } int nbns_rq_prepare(struct nbns_rq *rqp) { struct nb_ctx *ctx = rqp->nr_nbd; struct mbdata *mbp = &rqp->nr_rq; u_int8_t nmflags; u_char *cp; int len, error; error = mb_init(&rqp->nr_rp, NBDG_MAXSIZE); if (error) return error; if (rqp->nr_dest.sin_addr.s_addr == INADDR_BROADCAST) { rqp->nr_nmflags |= NBNS_NMFLAG_BCAST; if (nb_iflist == NULL) { error = nb_enum_if(&nb_iflist, 100); if (error) return error; } } else rqp->nr_nmflags &= ~NBNS_NMFLAG_BCAST; mb_put_uint16be(mbp, rqp->nr_trnid); nmflags = ((rqp->nr_opcode & 0x1F) << 3) | ((rqp->nr_nmflags & 0x70) >> 4); mb_put_uint8(mbp, nmflags); mb_put_uint8(mbp, (rqp->nr_nmflags & 0x0f) << 4 /* rcode */); mb_put_uint16be(mbp, rqp->nr_qdcount); mb_put_uint16be(mbp, rqp->nr_ancount); mb_put_uint16be(mbp, rqp->nr_nscount); mb_put_uint16be(mbp, rqp->nr_arcount); if (rqp->nr_qdcount) { if (rqp->nr_qdcount > 1) return EINVAL; len = nb_name_len(rqp->nr_qdname); error = mb_fit(mbp, len, (char**)&cp); if (error) return error; nb_name_encode(rqp->nr_qdname, cp); mb_put_uint16be(mbp, rqp->nr_qdtype); mb_put_uint16be(mbp, rqp->nr_qdclass); } m_lineup(mbp->mb_top, &mbp->mb_top); if (ctx->nb_timo == 0) ctx->nb_timo = 1; /* by default 1 second */ return 0; } static int nbns_rq_recv(struct nbns_rq *rqp) { struct mbdata *mbp = &rqp->nr_rp; void *rpdata = mtod(mbp->mb_top, void *); fd_set rd, wr, ex; struct timeval tv; struct sockaddr_in sender; int s = rqp->nr_fd; int n, len; FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); FD_SET(s, &rd); tv.tv_sec = rqp->nr_nbd->nb_timo; tv.tv_usec = 0; n = select(s + 1, &rd, &wr, &ex, &tv); if (n == -1) return -1; if (n == 0) return ETIMEDOUT; if (FD_ISSET(s, &rd) == 0) return ETIMEDOUT; len = sizeof(sender); n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0, (struct sockaddr*)&sender, &len); if (n < 0) return errno; mbp->mb_top->m_len = mbp->mb_count = n; rqp->nr_sender = sender; return 0; } static int nbns_rq_opensocket(struct nbns_rq *rqp) { struct sockaddr_in locaddr; int opt, s; s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return errno; if (rqp->nr_flags & NBRQF_BROADCAST) { opt = 1; if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) < 0) return errno; if (rqp->nr_if == NULL) return NBERROR(NBERR_NOBCASTIFS); bzero(&locaddr, sizeof(locaddr)); locaddr.sin_family = AF_INET; locaddr.sin_len = sizeof(locaddr); locaddr.sin_addr = rqp->nr_if->id_addr; rqp->nr_dest.sin_addr.s_addr = rqp->nr_if->id_addr.s_addr | ~rqp->nr_if->id_mask.s_addr; if (bind(s, (struct sockaddr*)&locaddr, sizeof(locaddr)) < 0) return errno; } return 0; } static int nbns_rq_send(struct nbns_rq *rqp) { struct mbdata *mbp = &rqp->nr_rq; int s = rqp->nr_fd; if (sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0, (struct sockaddr*)&rqp->nr_dest, sizeof(rqp->nr_dest)) < 0) return errno; return 0; } int nbns_rq(struct nbns_rq *rqp) { struct mbdata *mbp = &rqp->nr_rq; u_int16_t rpid; u_int8_t nmflags; int error, retrycount; rqp->nr_if = nb_iflist; again: error = nbns_rq_opensocket(rqp); if (error) return error; retrycount = 3; /* XXX - configurable */ for (;;) { error = nbns_rq_send(rqp); if (error) return error; error = nbns_rq_recv(rqp); if (error) { if (error != ETIMEDOUT || retrycount == 0) { if ((rqp->nr_nmflags & NBNS_NMFLAG_BCAST) && rqp->nr_if != NULL && rqp->nr_if->id_next != NULL) { rqp->nr_if = rqp->nr_if->id_next; close(rqp->nr_fd); goto again; } else return error; } retrycount--; continue; } mbp = &rqp->nr_rp; if (mbp->mb_count < 12) return NBERROR(NBERR_INVALIDRESPONSE); mb_get_uint16be(mbp, &rpid); if (rpid != rqp->nr_trnid) return NBERROR(NBERR_INVALIDRESPONSE); break; } mb_get_uint8(mbp, &nmflags); rqp->nr_rpnmflags = (nmflags & 7) << 4; mb_get_uint8(mbp, &nmflags); rqp->nr_rpnmflags |= (nmflags & 0xf0) >> 4; rqp->nr_rprcode = nmflags & 0xf; if (rqp->nr_rprcode) return NBERROR(rqp->nr_rprcode); mb_get_uint16be(mbp, &rpid); /* QDCOUNT */ mb_get_uint16be(mbp, &rqp->nr_rpancount); mb_get_uint16be(mbp, &rqp->nr_rpnscount); mb_get_uint16be(mbp, &rqp->nr_rparcount); return 0; } Index: stable/10 =================================================================== --- stable/10 (revision 335780) +++ stable/10 (revision 335781) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r335641