void reap_info_slave(void) { struct response_dgram resp; ssize_t len; char hostname[BUFFER_LEN], *hp; int n, count; conn_source source; if (info_slave_state != INFO_SLAVE_PENDING) { if (info_slave_state == INFO_SLAVE_DOWN) make_info_slave(); return; } len = recv(info_slave, &resp, sizeof resp, 0); if (len < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return; else if (len < 0 || len != (int) sizeof resp) { penn_perror("reading info_slave response"); return; } /* okay, now we have some info! */ if (!FD_ISSET(resp.fd, &info_pending)) { /* Duplicate or spoof. Ignore. */ return; } FD_CLR(resp.fd, &info_pending); /* See if we have any other pending queries and change state if not. */ for (n = 0, count = 0; n < pending_max; n++) if (FD_ISSET(n, &info_pending)) count++; if (count == 0) { info_slave_state = INFO_SLAVE_READY; pending_max = 0; } hp = hostname; if (resp.hostname[0]) safe_str(resp.hostname, hostname, &hp); else safe_str(resp.ipaddr, hostname, &hp); *hp = '\0'; if (Forbidden_Site(resp.ipaddr) || Forbidden_Site(hostname)) { if (!Deny_Silent_Site(resp.ipaddr, AMBIGUOUS) || !Deny_Silent_Site(hostname, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "[%d/%s/%s] Refused connection.", resp.fd, hostname, resp.ipaddr); } shutdown(resp.fd, 2); closesocket(resp.fd); return; } if (resp.connected_to == TINYPORT) source = CS_IP_SOCKET; else if (resp.connected_to == SSLPORT) source = CS_OPENSSL_SOCKET; else source = CS_UNKNOWN; do_log(LT_CONN, 0, 0, "[%d/%s/%s] Connection opened from %s.", resp.fd, hostname, resp.ipaddr, source_to_s(source)); set_keepalive(resp.fd, options.keepalive_timeout); initializesock(resp.fd, hostname, resp.ipaddr, source); }
/** Attempt to register a new player at the connect screen. * If registration is allowed, a new player object is created with * a random password which is emailed to the registering player. * \param name name of player to register. * \param email email address to send registration details. * \param host host from which registration is being attempted. * \param ip ip address from which registration is being attempted. * \return dbref of created player or NOTHING if creation failed. */ dbref email_register_player(DESC *d, const char *name, const char *email, const char *host, const char *ip) { char *p; char passwd[20]; static char elems[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; int i, len; bool resend = 0; dbref player = NOTHING; FILE *fp; size_t NELEMS = sizeof(elems) - 1; char sbuff[260]; if (!check_fails(ip)) { return NOTHING; } if (strlen(options.sendmail_prog) == 0) return NOTHING; if (!ok_player_name(name, NOTHING, NOTHING)) { /* Check for re-registration request */ player = lookup_player(name); if (GoodObject(player)) { ATTR *a; a = atr_get(player, "LASTLOGOUT"); if (!a) { a = atr_get(player, "REGISTERED_EMAIL"); if (a && !strcasecmp(atr_value(a), email)) resend = 1; } } if (!resend) { do_log(LT_CONN, 0, 0, "Failed registration (bad name) from %s", host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: bad name", name); return NOTHING; } } if (!resend) { /* Make sure that the email address is kind of valid. A valid * address must contain a @. Let the mailer sort it out beyond * that. Also, to prevent someone from using the MUSH to mailbomb * another site, let's make sure that the site to which the user * wants the email sent is also allowed to use the register * command. If there's an @, we check whatever's after the last @ * (since @foo.bar:user@host is a valid email). */ if ((p = strrchr(email, '@'))) { p++; if (!Site_Can_Register(p)) { if (!Deny_Silent_Site(p, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "Failed registration (bad site in email: %s) from %s", email, host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: bad site in email", name); } return NOTHING; } } else { if (!Deny_Silent_Site(host, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "Failed registration (bad email: %s) from %s", email, host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: sitelocked host", name); } return NOTHING; } if (DBTOP_MAX && (db_top >= DBTOP_MAX + 1) && (first_free == NOTHING)) { /* Oops, out of db space! */ do_log(LT_CONN, 0, 0, "Failed registration (no db space) from %s", host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, count_failed(ip), "register: no db space left to create!", name); return NOTHING; } } /* Come up with a random password of length 7-12 chars */ len = get_random_u32(7, 12); for (i = 0; i < len; i++) passwd[i] = elems[get_random_u32(0, NELEMS - 1)]; passwd[len] = '\0'; /* If we've made it here, we can send the email and create the * character. Email first, since that's more likely to go bad. * Some security precautions we'll take: * 1) We'll use sendmail -t, so we don't pass user-given values to a shell. * 2) We'll cross our fingers and hope nobody uses this to spam. */ release_fd(); snprintf(sbuff, sizeof sbuff, "%s -t", options.sendmail_prog); if ((fp = popen(sbuff, "w")) == NULL) { do_log(LT_CONN, 0, 0, "Failed registration of %s by %s: unable to open sendmail", name, email); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s,%d", d->descriptor, ip, count_failed(ip), "register: Unable to open sendmail!", name, 1); reserve_fd(); return NOTHING; } fprintf(fp, "Subject: "); fprintf(fp, T("[%s] Registration of %s\n"), MUDNAME, name); fprintf(fp, "To: %s\n", email); fprintf(fp, "Precedence: junk\n"); fprintf(fp, "\n"); fprintf(fp, T("This is an automated message.\n")); fprintf(fp, "\n"); fprintf(fp, T("Your requested player, %s, has been created.\n"), name); fprintf(fp, T("The password is %s\n"), passwd); fprintf(fp, "\n"); fprintf(fp, T("To access this character, connect to %s and type:\n"), MUDNAME); fprintf(fp, "\tconnect \"%s\" %s\n", name, passwd); fprintf(fp, "\n"); i = pclose(fp); reserve_fd(); if (i != 0) { /* Mailer exited with an error code. Log it. */ do_rawlog( LT_CONN, "When attempting to email a password to a newly registered player,\n" "\tthe mailer exited with error code %d.\n" "\t(Check /usr/include/sysexits.h if present for the meaning.)", i); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s,%d", d->descriptor, ip, count_failed(ip), "register: Unable to send email", name, i); return NOTHING; } else if (resend) { /* Reset the password */ (void) atr_add(player, pword_attr, password_hash(passwd, NULL), GOD, 0); return player; } else { /* Ok, all's well, make a player */ player = make_player(name, passwd, host, ip); queue_event(SYSEVENT, "PLAYER`CREATE", "%s,%s,%s,%d,%s", unparse_objid(player), name, "register", d->descriptor, email); (void) atr_add(player, "REGISTERED_EMAIL", email, GOD, 0); return player; } }
void query_info_slave(int fd) { struct request_dgram req; struct hostname_info *hi; char buf[BUFFER_LEN], *bp; ssize_t slen; FD_SET(fd, &info_pending); if (fd > pending_max) pending_max = fd + 1; info_queue_time = time(NULL); if (info_slave_state == INFO_SLAVE_DOWN) { if (!make_info_slave()) { FD_CLR(fd, &info_pending); closesocket(fd); /* Just drop the connection if the slave gets halted. A subsequent reconnect will work. */ } return; } memset(&req, 0, sizeof req); req.rlen = MAXSOCKADDR; if (getpeername(fd, (struct sockaddr *) req.remote.data, &req.rlen) < 0) { penn_perror("socket peer vanished"); shutdown(fd, 2); closesocket(fd); FD_CLR(fd, &info_pending); return; } /* Check for forbidden sites before bothering with ident */ bp = buf; hi = ip_convert(&req.remote.addr, req.rlen); safe_str(hi ? hi->hostname : "Not found", buf, &bp); *bp = '\0'; if (Forbidden_Site(buf)) { char port[NI_MAXSERV]; if (getnameinfo(&req.remote.addr, req.rlen, NULL, 0, port, sizeof port, NI_NUMERICHOST | NI_NUMERICSERV) != 0) penn_perror("getting remote port number"); else { if (!Deny_Silent_Site(buf, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "[%d/%s] Refused connection (remote port %s)", fd, buf, port); } } closesocket(fd); FD_CLR(fd, &info_pending); return; } req.llen = MAXSOCKADDR; if (getsockname(fd, (struct sockaddr *) req.local.data, &req.llen) < 0) { penn_perror("socket self vanished"); closesocket(fd); FD_CLR(fd, &info_pending); return; } req.fd = fd; req.use_dns = USE_DNS; slen = send(info_slave, &req, sizeof req, 0); if (slen < 0) { penn_perror("info slave query: write error"); make_info_slave(); return; } else if (slen != (int) sizeof req) { /* Shouldn't happen! */ penn_perror("info slave query: partial packet"); make_info_slave(); return; } info_slave_state = INFO_SLAVE_PENDING; }
/** Check to see if someone can connect to a player. * \param d DESC the connect attempt is being made for * \param name name of player to connect to. * \param password password of player to connect to. * \param host host from which connection is being attempted. * \param ip ip address from which connection is being attempted. * \param errbuf buffer to return connection errors. * \return dbref of connected player object or NOTHING for failure * (with reason for failure returned in errbuf). */ dbref connect_player(DESC *d, const char *name, const char *password, const char *host, const char *ip, char *errbuf) { dbref player; int count; /* Default error */ strcpy(errbuf, T("Either that player does not exist, or has a different password.")); if (!name || !*name) return NOTHING; /* validate name */ if ((player = lookup_player(name)) == NOTHING) { /* Invalid player names are failures, too. */ count = mark_failed(ip); strcpy(errbuf, T("There is no player with that name.")); queue_event(SYSEVENT, "SOCKET`LOGINFAIL", "%d,%s,%d,%s,#%d,%s", d->descriptor, ip, count, "invalid player", -1, name); return NOTHING; } /* See if player is allowed to connect like this */ if (Going(player) || Going_Twice(player)) { do_log(LT_CONN, 0, 0, "Connection to GOING player %s not allowed from %s (%s)", Name(player), host, ip); queue_event(SYSEVENT, "SOCKET`LOGINFAIL", "%d,%s,%d,%s,#%d", d->descriptor, ip, count_failed(ip), "player is going", player); strcpy(errbuf, T("You cannot connect to that player at this time.")); return NOTHING; } /* Check sitelock patterns */ if (Guest(player) && (!Site_Can_Guest(host, player) || !Site_Can_Guest(ip, player))) { if (!Deny_Silent_Site(host, AMBIGUOUS) && !Deny_Silent_Site(ip, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "Connection to %s (GUEST) not allowed from %s (%s)", name, host, ip); strcpy(errbuf, T("Guest connections not allowed.")); count = mark_failed(ip); queue_event(SYSEVENT, "SOCKET`LOGINFAIL", "%d,%s,%d,%s,#%d", d->descriptor, ip, count, "failed sitelock", player); } return NOTHING; } else if (!Guest(player) && (!Site_Can_Connect(host, player) || !Site_Can_Connect(ip, player))) { if (!Deny_Silent_Site(host, player) && !Deny_Silent_Site(ip, player)) { do_log(LT_CONN, 0, 0, "Connection to %s (Non-GUEST) not allowed from %s (%s)", name, host, ip); strcpy(errbuf, T("Player connections not allowed.")); count = mark_failed(ip); queue_event(SYSEVENT, "SOCKET`LOGINFAIL", "%d,%s,%d,%s,#%d", d->descriptor, ip, count, "failed sitelock", player); } return NOTHING; } /* validate password */ if (!Guest(player)) if (!password_check(player, password)) { /* Increment count of login failures */ ModTime(player)++; check_lastfailed(player, host); count = mark_failed(ip); queue_event(SYSEVENT, "SOCKET`LOGINFAIL", "%d,%s,%d,%s,#%d", d->descriptor, ip, count, "invalid password", player); strcpy(errbuf, T("That is not the correct password.")); return NOTHING; } /* If it's a Guest player, and already connected, search the * db for another Guest player to connect them to. */ if (Guest(player)) { /* Enforce guest limit */ player = guest_to_connect(player); if (!GoodObject(player)) { do_log(LT_CONN, 0, 0, "Can't connect to a guest (too many connected)"); strcpy(errbuf, T("Too many guests are connected now.")); queue_event(SYSEVENT, "SOCKET`LOGINFAIL", "%d,%s,%d,%s,#%d", d->descriptor, ip, count_failed(ip), "too many guests", player); return NOTHING; } } if (Suspect_Site(host, player) || Suspect_Site(ip, player)) { do_log(LT_CONN, 0, 0, "Connection from Suspect site. Setting %s(#%d) suspect.", Name(player), player); set_flag_internal(player, "SUSPECT"); } return player; }