/* register a name with our WINS servers */ void nbtd_winsclient_register(struct nbtd_iface_name *iname) { struct nbtd_interface *iface = iname->iface; struct nbt_name_register_wins io; struct composite_context *c; /* setup a wins name register request */ io.in.name = iname->name; io.in.wins_port = lp_nbt_port(iname->iface->nbtsrv->task->lp_ctx); io.in.wins_servers = lp_wins_server_list(iname->iface->nbtsrv->task->lp_ctx); io.in.addresses = nbtd_address_list(iface, iname); io.in.nb_flags = iname->nb_flags; io.in.ttl = iname->ttl; if (!io.in.addresses) { return; } c = nbt_name_register_wins_send(wins_socket(iface), &io); if (c == NULL) { talloc_free(io.in.addresses); return; } talloc_steal(c, io.in.addresses); c->async.fn = nbtd_wins_register_handler; c->async.private_data = iname; }
/* refresh a WINS name registration */ static void nbtd_wins_refresh(struct tevent_context *ev, struct tevent_timer *te, struct timeval t, void *private_data) { struct nbtd_iface_name *iname = talloc_get_type(private_data, struct nbtd_iface_name); struct nbtd_interface *iface = iname->iface; struct nbt_name_refresh_wins io; struct composite_context *c; TALLOC_CTX *tmp_ctx = talloc_new(iname); /* setup a wins name refresh request */ io.in.name = iname->name; io.in.wins_servers = (const char **)str_list_make_single(tmp_ctx, iname->wins_server); io.in.wins_port = lp_nbt_port(iface->nbtsrv->task->lp_ctx); io.in.addresses = nbtd_address_list(iface, tmp_ctx); io.in.nb_flags = iname->nb_flags; io.in.ttl = iname->ttl; if (!io.in.addresses) { talloc_free(tmp_ctx); return; } c = nbt_name_refresh_wins_send(wins_socket(iface), &io); if (c == NULL) { talloc_free(tmp_ctx); return; } talloc_steal(c, io.in.addresses); c->async.fn = nbtd_wins_refresh_handler; c->async.private_data = iname; talloc_free(tmp_ctx); }
/* a client has asked to register a unique name that someone else owns. We need to ask each of the current owners if they still want it. If they do then reject the registration, otherwise allow it */ static void wins_register_wack(struct nbt_name_socket *nbtsock, struct nbt_name_packet *packet, struct winsdb_record *rec, struct socket_address *src, enum wrepl_name_type new_type) { struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data, struct nbtd_interface); struct wins_server *winssrv = iface->nbtsrv->winssrv; struct nbtd_wins_wack_state *s; struct composite_context *c_req; uint32_t ttl; s = talloc_zero(nbtsock, struct nbtd_wins_wack_state); if (s == NULL) goto failed; /* package up the state variables for this wack request */ s->winssrv = winssrv; s->nbtsock = nbtsock; s->iface = iface; s->request_packet = talloc_steal(s, packet); s->rec = talloc_steal(s, rec); s->reg_address = packet->additional[0].rdata.netbios.addresses[0].ipaddr; s->new_type = new_type; s->src = src; if (talloc_reference(s, src) == NULL) goto failed; s->io.in.nbtd_server = iface->nbtsrv; s->io.in.nbt_port = lp_nbt_port(iface->nbtsrv->task->lp_ctx); s->io.in.event_ctx = iface->nbtsrv->task->event_ctx; s->io.in.name = rec->name; s->io.in.num_addresses = winsdb_addr_list_length(rec->addresses); s->io.in.addresses = winsdb_addr_string_list(s, rec->addresses); if (s->io.in.addresses == NULL) goto failed; DLIST_ADD_END(iface->wack_queue, s, struct nbtd_wins_wack_state *); talloc_set_destructor(s, nbtd_wins_wack_state_destructor); /* * send a WACK to the client, specifying the maximum time it could * take to check with the owner, plus some slack */ ttl = 5 + 4 * winsdb_addr_list_length(rec->addresses); nbtd_wack_reply(nbtsock, packet, src, ttl); /* * send the challenge to the old addresses */ c_req = wins_challenge_send(s, &s->io); if (c_req == NULL) goto failed; c_req->async.fn = wack_wins_challenge_handler; c_req->async.private_data = s; return; failed: talloc_free(s); nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_SVR); }
BOOL nbtd_self_packet(struct nbt_name_socket *nbtsock, struct nbt_name_packet *packet, const struct socket_address *src) { struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, struct nbtd_interface); struct nbtd_server *nbtsrv = iface->nbtsrv; /* if its not from the nbt port, then it wasn't a broadcast from us */ if (src->port != lp_nbt_port()) { return False; } /* we have to loop over our interface list, seeing if its from one of our own interfaces */ for (iface=nbtsrv->interfaces;iface;iface=iface->next) { if (strcmp(src->addr, iface->ip_address) == 0) { return True; } } return False; }
/* main program */ int main(int argc, const char *argv[]) { bool ret = true; struct interface *ifaces; struct tevent_context *ev; poptContext pc; int opt; enum { OPT_BROADCAST_ADDRESS = 1000, OPT_UNICAST_ADDRESS, OPT_FIND_MASTER, OPT_WINS_LOOKUP, OPT_NODE_STATUS, OPT_ROOT_PORT, OPT_LOOKUP_BY_IP, OPT_CASE_SENSITIVE }; struct poptOption long_options[] = { POPT_AUTOHELP { "broadcast", 'B', POPT_ARG_STRING, NULL, OPT_BROADCAST_ADDRESS, "Specify address to use for broadcasts", "BROADCAST-ADDRESS" }, { "unicast", 'U', POPT_ARG_STRING, NULL, OPT_UNICAST_ADDRESS, "Specify address to use for unicast", NULL }, { "master-browser", 'M', POPT_ARG_NONE, NULL, OPT_FIND_MASTER, "Search for a master browser", NULL }, { "wins", 'W', POPT_ARG_NONE, NULL, OPT_WINS_LOOKUP, "Do a WINS lookup", NULL }, { "status", 'S', POPT_ARG_NONE, NULL, OPT_NODE_STATUS, "Lookup node status as well", NULL }, { "root-port", 'r', POPT_ARG_NONE, NULL, OPT_ROOT_PORT, "Use root port 137 (Win95 only replies to this)", NULL }, { "lookup-by-ip", 'A', POPT_ARG_NONE, NULL, OPT_LOOKUP_BY_IP, "Do a node status on <name> as an IP Address", NULL }, { "case-sensitive", 0, POPT_ARG_NONE, NULL, OPT_CASE_SENSITIVE, "Don't uppercase the name before sending", NULL }, POPT_COMMON_SAMBA { 0, 0, 0, 0 } }; pc = poptGetContext("nmblookup", argc, argv, long_options, POPT_CONTEXT_KEEP_FIRST); poptSetOtherOptionHelp(pc, "<NODE> ..."); while ((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { case OPT_BROADCAST_ADDRESS: options.broadcast_address = poptGetOptArg(pc); break; case OPT_UNICAST_ADDRESS: options.unicast_address = poptGetOptArg(pc); break; case OPT_FIND_MASTER: options.find_master = true; break; case OPT_WINS_LOOKUP: options.wins_lookup = true; break; case OPT_NODE_STATUS: options.node_status = true; break; case OPT_ROOT_PORT: options.root_port = true; break; case OPT_LOOKUP_BY_IP: options.lookup_by_ip = true; break; case OPT_CASE_SENSITIVE: options.case_sensitive = true; break; } } /* swallow argv[0] */ poptGetArg(pc); if(!poptPeekArg(pc)) { poptPrintUsage(pc, stderr, 0); exit(1); } load_interfaces(NULL, lp_interfaces(cmdline_lp_ctx), &ifaces); ev = s4_event_context_init(talloc_autofree_context()); while (poptPeekArg(pc)) { const char *name = poptGetArg(pc); ret &= process_one(cmdline_lp_ctx, ev, ifaces, name, lp_nbt_port(cmdline_lp_ctx)); } talloc_free(ev); talloc_free(ifaces); poptFreeContext(pc); if (!ret) { return 1; } return 0; }
/* start listening on the given address */ static NTSTATUS nbtd_add_socket(struct nbtd_server *nbtsrv, struct loadparm_context *lp_ctx, const char *bind_address, const char *address, const char *bcast, const char *netmask) { struct nbtd_interface *iface; NTSTATUS status; struct socket_address *bcast_address; struct socket_address *unicast_address; DEBUG(6,("nbtd_add_socket(%s, %s, %s, %s)\n", bind_address, address, bcast, netmask)); /* we actually create two sockets. One listens on the broadcast address for the interface, and the other listens on our specific address. This allows us to run with "bind interfaces only" while still receiving broadcast addresses, and also simplifies matching incoming requests to interfaces */ iface = talloc(nbtsrv, struct nbtd_interface); NT_STATUS_HAVE_NO_MEMORY(iface); iface->nbtsrv = nbtsrv; iface->bcast_address = talloc_steal(iface, bcast); iface->ip_address = talloc_steal(iface, address); iface->netmask = talloc_steal(iface, netmask); iface->names = NULL; iface->wack_queue = NULL; if (strcmp(netmask, "0.0.0.0") != 0) { struct nbt_name_socket *bcast_nbtsock; /* listen for broadcasts on port 137 */ bcast_nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx, lp_iconv_convenience(nbtsrv->task->lp_ctx)); if (!bcast_nbtsock) { talloc_free(iface); return NT_STATUS_NO_MEMORY; } bcast_address = socket_address_from_strings(bcast_nbtsock, bcast_nbtsock->sock->backend_name, bcast, lp_nbt_port(lp_ctx)); if (!bcast_address) { talloc_free(iface); return NT_STATUS_NO_MEMORY; } status = socket_listen(bcast_nbtsock->sock, bcast_address, 0, 0); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("Failed to bind to %s:%d - %s\n", bcast, lp_nbt_port(lp_ctx), nt_errstr(status))); talloc_free(iface); return status; } talloc_free(bcast_address); nbt_set_incoming_handler(bcast_nbtsock, nbtd_request_handler, iface); } /* listen for unicasts on port 137 */ iface->nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx, lp_iconv_convenience(nbtsrv->task->lp_ctx)); if (!iface->nbtsock) { talloc_free(iface); return NT_STATUS_NO_MEMORY; } unicast_address = socket_address_from_strings(iface->nbtsock, iface->nbtsock->sock->backend_name, bind_address, lp_nbt_port(lp_ctx)); status = socket_listen(iface->nbtsock->sock, unicast_address, 0, 0); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("Failed to bind to %s:%d - %s\n", bind_address, lp_nbt_port(lp_ctx), nt_errstr(status))); talloc_free(iface); return status; } talloc_free(unicast_address); nbt_set_incoming_handler(iface->nbtsock, nbtd_request_handler, iface); nbt_set_unexpected_handler(iface->nbtsock, nbtd_unexpected_handler, iface); /* also setup the datagram listeners */ status = nbtd_dgram_setup(iface, bind_address); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("Failed to setup dgram listen on %s - %s\n", bind_address, nt_errstr(status))); talloc_free(iface); return status; } if (strcmp(netmask, "0.0.0.0") == 0) { DLIST_ADD(nbtsrv->bcast_interface, iface); } else { DLIST_ADD(nbtsrv->interfaces, iface); } return NT_STATUS_OK; }
/* test operations against a WINS server */ static bool nbt_test_wins_name(struct torture_context *tctx, const char *address, struct nbt_name *name, uint16_t nb_flags, bool try_low_port) { struct nbt_name_register_wins io; struct nbt_name_register name_register; struct nbt_name_query query; struct nbt_name_refresh_wins refresh; struct nbt_name_release release; struct nbt_name_request *req; NTSTATUS status; struct nbt_name_socket *nbtsock = torture_init_nbt_socket(tctx); const char *myaddress; struct socket_address *socket_address; struct interface *ifaces; bool low_port = try_low_port; load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces); myaddress = talloc_strdup(tctx, iface_best_ip(ifaces, address)); socket_address = socket_address_from_strings(tctx, nbtsock->sock->backend_name, myaddress, lp_nbt_port(tctx->lp_ctx)); torture_assert(tctx, socket_address != NULL, "Error getting address"); /* we do the listen here to ensure the WINS server receives the packets from the right IP */ status = socket_listen(nbtsock->sock, socket_address, 0, 0); talloc_free(socket_address); if (!NT_STATUS_IS_OK(status)) { low_port = false; socket_address = socket_address_from_strings(tctx, nbtsock->sock->backend_name, myaddress, 0); torture_assert(tctx, socket_address != NULL, "Error getting address"); status = socket_listen(nbtsock->sock, socket_address, 0, 0); talloc_free(socket_address); torture_assert_ntstatus_ok(tctx, status, "socket_listen for WINS failed"); } torture_comment(tctx, "Testing name registration to WINS with name %s at %s nb_flags=0x%x\n", nbt_name_string(tctx, name), myaddress, nb_flags); torture_comment(tctx, "release the name\n"); release.in.name = *name; release.in.dest_port = lp_nbt_port(tctx->lp_ctx); release.in.dest_addr = address; release.in.address = myaddress; release.in.nb_flags = nb_flags; release.in.broadcast = false; release.in.timeout = 3; release.in.retries = 0; status = nbt_name_release(nbtsock, tctx, &release); torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name query", address)); CHECK_VALUE(tctx, release.out.rcode, 0); if (nb_flags & NBT_NM_GROUP) { /* ignore this for group names */ } else if (!low_port) { torture_comment(tctx, "no low port - skip: register the name with a wrong address\n"); } else { torture_comment(tctx, "register the name with a wrong address (makes the next request slow!)\n"); io.in.name = *name; io.in.wins_port = lp_nbt_port(tctx->lp_ctx); io.in.wins_servers = str_list_make(tctx, address, NULL); io.in.addresses = str_list_make(tctx, "127.64.64.1", NULL); io.in.nb_flags = nb_flags; io.in.ttl = 300000; status = nbt_name_register_wins(nbtsock, tctx, &io); if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "No response from %s for name register\n", address)); } torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register\n", address)); CHECK_STRING(tctx, io.out.wins_server, address); CHECK_VALUE(tctx, io.out.rcode, 0); torture_comment(tctx, "register the name correct address\n"); name_register.in.name = *name; name_register.in.dest_port = lp_nbt_port(tctx->lp_ctx); name_register.in.dest_addr = address; name_register.in.address = myaddress; name_register.in.nb_flags = nb_flags; name_register.in.register_demand= false; name_register.in.broadcast = false; name_register.in.multi_homed = true; name_register.in.ttl = 300000; name_register.in.timeout = 3; name_register.in.retries = 2; /* * test if the server ignores resent requests */ req = nbt_name_register_send(nbtsock, &name_register); while (true) { event_loop_once(nbtsock->event_ctx); if (req->state != NBT_REQUEST_WAIT) { break; } if (req->received_wack) { /* * if we received the wack response * we resend the request and the * server should ignore that * and not handle it as new request */ req->state = NBT_REQUEST_SEND; DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *); EVENT_FD_WRITEABLE(nbtsock->fde); break; } } status = nbt_name_register_recv(req, tctx, &name_register); if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "No response from %s for name register\n", address)); } torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register\n", address)); CHECK_VALUE(tctx, name_register.out.rcode, 0); CHECK_STRING(tctx, name_register.out.reply_addr, myaddress); }
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "No response from %s for name register\n", address)); } torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register\n", address)); CHECK_VALUE(tctx, name_register.out.rcode, 0); CHECK_STRING(tctx, name_register.out.reply_addr, myaddress); } torture_comment(tctx, "register the name correct address\n"); io.in.name = *name; io.in.wins_port = lp_nbt_port(tctx->lp_ctx); io.in.wins_servers = (const char **)str_list_make(tctx, address, NULL); io.in.addresses = (const char **)str_list_make(tctx, myaddress, NULL); io.in.nb_flags = nb_flags; io.in.ttl = 300000; status = nbt_name_register_wins(nbtsock, tctx, &io); torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "Bad response from %s for name register", address)); CHECK_STRING(tctx, io.out.wins_server, address); CHECK_VALUE(tctx, io.out.rcode, 0); if (name->type != NBT_NAME_MASTER && name->type != NBT_NAME_LOGON && name->type != NBT_NAME_BROWSER && (nb_flags & NBT_NM_GROUP)) {