/************************************************************************************************** READ_TCP_QUERY Returns 0 on success, -1 on failure. **************************************************************************************************/ int read_tcp_query(TASK *t) { unsigned char *end; int rv; /* Read packet length if we haven't already */ if (!t->len) return read_tcp_length(t); end = t->query + t->len; /* Read whatever data is ready */ if ((rv = recv(t->fd, t->query + t->offset, t->len - t->offset, 0)) < 0) return Warn("%s: %s", clientaddr(t), _("recv (TCP)")); if (!rv) return (-1); /* Client closed connection */ #if DEBUG_ENABLED && DEBUG_TCP Debug("%s: 2+%d TCP octets in", clientaddr(t), rv); #endif t->offset += rv; if (t->offset > t->len) return Warnx("%s: %s", clientaddr(t), _("TCP message data too long")); if (t->offset < t->len) return 0; /* Not finished reading */ t->offset = 0; /* Reset offset for writing reply */ return new_task(t, t->query, t->len); }
/************************************************************************************************** READ_TCP_LENGTH The first two octets of a TCP question are the length. Read them. Returns 0 on success, -1 on failure. **************************************************************************************************/ static int read_tcp_length(TASK *t) { int rv; char len[2]; if ((rv = recv(t->fd, len, 2, 0)) != 2) { if (rv < 0) { if (errno == EAGAIN) return (0); if (errno != ECONNRESET) Warn("%s: %s", clientaddr(t), _("recv (length) (TCP)")); return (-1); } if (rv == 0) return (-1); return Warnx("%s: %s", clientaddr(t), _("TCP message length invalid")); } if ((t->len = ((len[0] << 8) | (len[1]))) < DNS_HEADERSIZE) return Warnx("%s: %s (%d octet%s)", clientaddr(t), _("TCP message too short"), t->len, S(t->len)); if (t->len > DNS_MAXPACKETLEN_TCP) return Warnx("%s: %s (%d octet%s)", clientaddr(t), _("TCP message too long"), t->len, S(t->len)); if (!(t->query = calloc(1, t->len + 1))) Err(_("out of memory")); t->offset = 0; return (0); }
/************************************************************************************************** WRITE_TCP_LENGTH Writes the length octets for TCP reply. Returns 0 on success, -1 on failure. **************************************************************************************************/ static int write_tcp_length(TASK *t) { char len[2], *l; int rv; l = len; DNS_PUT16(l, t->replylen); if ((rv = write(t->fd, len + t->offset, SIZE16 - t->offset)) < 0) { if (errno == EINTR) return 0; return Warn("%s: %s", clientaddr(t), _("write (length) (TCP)")); } if (!rv) return (-1); /* Client closed connection */ t->offset += rv; if (t->offset >= SIZE16) { t->len_written = 1; t->offset = 0; } return 0; }
/************************************************************************************************** READ_UDP_QUERY Returns 0 on success (a task was added), -1 on failure. **************************************************************************************************/ taskexec_t read_udp_query(int fd, int family) { struct sockaddr addr; char in[DNS_MAXPACKETLEN_UDP]; socklen_t addrlen = 0; int len = 0; TASK *t = NULL; taskexec_t rv = TASK_FAILED; memset(&addr, 0, sizeof(addr)); memset(&in, 0, sizeof(in)); /* Read message */ if (family == AF_INET) { addrlen = sizeof(struct sockaddr_in); #if HAVE_IPV6 } else if (family == AF_INET6) { addrlen = sizeof(struct sockaddr_in6); #endif } if ((len = recvfrom(fd, &in, sizeof(in), 0, &addr, &addrlen)) < 0) { if ( (errno == EINTR) #ifdef EAGAIN || (errno == EAGAIN) #else #ifdef EWOULDBLOCK || (errno == EWOULDBLOCK) #endif #endif ) { return (TASK_CONTINUE); } return Warn("%s", _("recvfrom (UDP)")); } if (len == 0) { return (TASK_FAILED); } if (!(t = IOtask_init(HIGH_PRIORITY_TASK, NEED_ANSWER, fd, SOCK_DGRAM, family, &addr))) return (TASK_FAILED); #if DEBUG_ENABLED && DEBUG_UDP DebugX("udp", 1, "%s: %d %s", clientaddr(t), len, _("UDP octets in")); #endif rv = task_new(t, (unsigned char*)in, len); if (rv < TASK_FAILED) { dequeue(t); rv = TASK_FAILED; } return rv; }
/* Begin-Doc Name: thr_watch_port Description: thread handler to listen to a tcp port Syntax: thr_watch_port(port_in_string_form) End-Doc */ void *thr_watch_port(void *threadarg) { int sockfd; int server_port; struct sockaddr_in cli_addr; unsigned int clilen; int newsockfd; char *cliaddr; server_port = atoi((char *)threadarg); Debug(("opening listener socket on port %d\n", server_port)); syslog(LOG_DEBUG, "opening listener socket on port %d", server_port); sockfd = OpenListener(server_port, 10); if (!sockfd) { Error(("Unable to open listener port! (%s)\n", strerror(errno))); syslog(LOG_ERR, "unable to open listener port (%s)\n", strerror(errno)); exit(1); } Debug(("opened listener socket on port %d\n", server_port)); syslog(LOG_DEBUG, "opened listener socket on port %d", server_port); while (1) { clilen = sizeof(cli_addr); Debug(("waiting for socket connection\n")); syslog(LOG_DEBUG, "waiting for socket connection"); newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen); if (newsockfd) { syslog(LOG_DEBUG, "got a socket connection"); Debug(("got a socket connection\n")); cliaddr = clientaddr(newsockfd); syslog(LOG_DEBUG, "got a socket connection on port %d from %s, triggering updates", server_port, NullCk(cliaddr)); if (cliaddr) { free(cliaddr); } mark_updated(); close(newsockfd); } else { syslog(LOG_ERR, "error accepting socket connection (%s)", strerror(errno)); } } }
// Test1 - just do a basic binding request HRESULT CTestMessageHandler::Test1() { HRESULT hr=S_OK; CStunMessageBuilder builder; CSocketAddress clientaddr(0x12345678, 9876); CRefCountedBuffer spBuffer; CStunThreadMessageHandler handler; CStunMessageReader reader; CStunMessageReader::ReaderParseState state; StunMessageEnvelope message; _spTransport->Reset(); _spTransport->AddPP(CSocketAddress(0xaaaaaaaa, 1234)); InitBindingRequest(builder); builder.GetStream().GetBuffer(&spBuffer); handler.SetResponder(_spTransport); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->GetOutputStream().GetBuffer(&spBuffer); state = reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); // validate that the binding response matches our expectations ChkA(ValidateMappedAddress(reader, clientaddr)); // validate that it came from the server port we expected ChkA(ValidateOriginAddress(reader, RolePP)); // did we get back the binding request we expected ChkA(ValidateResponseAddress(clientaddr)); Cleanup: return hr; }
/************************************************************************************************** CHECK_XFER If the "xfer" column exists in the soa table, it should contain a list of wildcards separated by commas. In order for this zone transfer to continue, one of the wildcards must match the client's IP address. **************************************************************************************************/ static void check_xfer(TASK *t, MYDNS_SOA *soa) { SQL_RES *res = NULL; SQL_ROW row = NULL; char ip[256]; char *query = NULL; size_t querylen = 0; int ok = 0; memset(&ip, 0, sizeof(ip)); if (!mydns_soa_use_xfer) return; strncpy(ip, clientaddr(t), sizeof(ip)-1); querylen = sql_build_query(&query, "SELECT xfer FROM %s WHERE id=%u%s%s%s;", mydns_soa_table_name, soa->id, (mydns_rr_use_active)? " AND active='" : "", (mydns_rr_use_active)? mydns_rr_active_types[0] : "", (mydns_rr_use_active)? "'" : ""); res = sql_query(sql, query, querylen); RELEASE(query); if (!res) { ErrSQL(sql, "%s: %s", desctask(t), _("error loading zone transfer access rules")); } if ((row = sql_getrow(res, NULL))) { char *wild = NULL, *r = NULL; for (r = row[0]; !ok && (wild = strsep(&r, ",")); ) { if (strchr(wild, '/')) { if (t->family == AF_INET) ok = in_cidr(wild, t->addr4.sin_addr); } else if (wildcard_match(wild, ip)) ok = 1; } } sql_free(res); if (!ok) { dnserror(t, DNS_RCODE_REFUSED, ERR_NO_AXFR); axfr_reply(t); axfr_error(t, _("access denied")); } }
/************************************************************************************************** ACCEPT_TCP_QUERY **************************************************************************************************/ int accept_tcp_query(int fd, int family) { struct sockaddr_in addr4; #if HAVE_IPV6 struct sockaddr_in6 addr6; #endif socklen_t addrlen; int rmt_fd; TASK *t; #if HAVE_IPV6 if (family == AF_INET6) { addrlen = sizeof(struct sockaddr_in6); if ((rmt_fd = accept(fd, (struct sockaddr *)&addr6, &addrlen)) < 0) { return Warn("%s", _("accept (TCPv6)")); } fcntl(rmt_fd, F_SETFL, fcntl(rmt_fd, F_GETFL, 0) | O_NONBLOCK); if (!(t = task_init(NEED_READ, rmt_fd, SOCK_STREAM, AF_INET6, &addr6))) return (-1); } else #endif { addrlen = sizeof(struct sockaddr_in); if ((rmt_fd = accept(fd, (struct sockaddr *)&addr4, &addrlen)) < 0) { return Warn("%s", _("accept (TCP)")); } fcntl(rmt_fd, F_SETFL, fcntl(rmt_fd, F_GETFL, 0) | O_NONBLOCK); if (!(t = task_init(NEED_READ, rmt_fd, SOCK_STREAM, AF_INET, &addr4))) return (-1); } #if DEBUG_ENABLED && DEBUG_TCP Debug("%s: TCP connection accepted", clientaddr(t)); #endif return 0; }
/************************************************************************************************** WRITE_TCP_REPLY Returns 0 on success, -1 on error. If -1 is returned, the task is no longer valid. **************************************************************************************************/ int write_tcp_reply(TASK *t) { int rv, rmt_fd; struct sockaddr_in addr4; #if HAVE_IPV6 struct sockaddr_in6 addr6; #endif /* Write TCP length if we haven't already */ if (!t->len_written) { if (write_tcp_length(t) < 0) { dequeue(Tasks, t); return (-1); } return (0); } /* Write the reply */ if ((rv = write(t->fd, t->reply + t->offset, t->replylen - t->offset)) < 0) { if (errno == EINTR) return (0); dequeue(Tasks, t); return Warn("%s: %s", clientaddr(t), _("write (TCP)")); } if (!rv) { dequeue(Tasks, t); return (-1); /* Client closed connection */ } t->offset += rv; if (t->offset < t->replylen) return (0); /* Not finished yet... */ /* Task complete; reset. The TCP client must be able to perform multiple queries on the same connection (BIND8 AXFR does this for sure) */ #if HAVE_IPV6 if (t->family == AF_INET6) { memcpy(&addr6, &t->addr6, sizeof(struct sockaddr_in6)); rmt_fd = t->fd; dequeue(Tasks, t); /* Reinitialize to allow multiple queries on TCP */ if (!(t = task_init(NEED_READ, rmt_fd, SOCK_STREAM, AF_INET6, &addr6))) return (-2); } else #endif { memcpy(&addr4, &t->addr4, sizeof(struct sockaddr_in)); rmt_fd = t->fd; dequeue(Tasks, t); /* Reinitialize to allow multiple queries on TCP */ if (!(t = task_init(NEED_READ, rmt_fd, SOCK_STREAM, AF_INET, &addr4))) return (-2); } return (0); }
// test long-credential authentication HRESULT CTestMessageHandler::Test4() { HRESULT hr=S_OK; CStunMessageBuilder builder1, builder2; CStunMessageReader reader1, reader2; CSocketAddress clientaddr(0x12345678, 9876); CSocketAddress addrMapped; CRefCountedBuffer spBuffer; CStunThreadMessageHandler handler; uint16_t errorcode = 0; char szNonce[MAX_STUN_AUTH_STRING_SIZE+1]; char szRealm[MAX_STUN_AUTH_STRING_SIZE+1]; CStunMessageReader::ReaderParseState state; StunMessageEnvelope message; _spTransport->Reset(); _spTransport->AddPP(CSocketAddress(0xaaaaaaaa, 1234)); handler.SetAuth(_spAuthLong); handler.SetResponder(_spTransport); // ----------------------------------------------------------------------- // simulate a user making a request with no message integrity attribute (or username, or realm) InitBindingRequest(builder1); builder1.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader1.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); // we expect the response back will be a 401 with a provided nonce and realm Chk(reader1.GetErrorCode(&errorcode)); ChkIfA(reader1.GetMessageClass() != ::StunMsgClassFailureResponse, E_UNEXPECTED); ChkIf(errorcode != ::STUN_ERROR_UNAUTHORIZED, E_UNEXPECTED); reader1.GetStringAttributeByType(STUN_ATTRIBUTE_REALM, szRealm, ARRAYSIZE(szRealm)); reader1.GetStringAttributeByType(STUN_ATTRIBUTE_NONCE, szNonce, ARRAYSIZE(szNonce)); // -------------------------------------------------------------------------------- // now simulate the follow-up request _spTransport->ClearStream(); spBuffer.reset(); InitBindingRequest(builder2); builder2.AddNonce(szNonce); builder2.AddRealm(szRealm); builder2.AddUserName("AuthorizedUser"); builder2.AddMessageIntegrityLongTerm("AuthorizedUser", szRealm, "password"); builder2.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader2.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); ChkIfA(reader2.GetMessageClass() != ::StunMsgClassSuccessResponse, E_UNEXPECTED); // should have a mapped address ChkA(reader2.GetMappedAddress(&addrMapped)); // and the message integrity field should be valid ChkA(reader2.ValidateMessageIntegrityLong("AuthorizedUser", szRealm, "password")); Cleanup: return hr; }
// test simple authentication HRESULT CTestMessageHandler::Test3() { HRESULT hr=S_OK; CStunMessageBuilder builder1, builder2, builder3; CStunMessageReader reader1, reader2, reader3; CSocketAddress clientaddr(0x12345678, 9876); CRefCountedBuffer spBuffer; CStunThreadMessageHandler handler; uint16_t errorcode = 0; CStunMessageReader::ReaderParseState state; StunMessageEnvelope message; _spTransport->Reset(); _spTransport->AddPP(CSocketAddress(0xaaaaaaaa, 1234)); handler.SetAuth(_spAuthShort); handler.SetResponder(_spTransport); // ----------------------------------------------------------------------- // simulate an authorized user making a request with a valid password InitBindingRequest(builder1); builder1.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "AuthorizedUser"); builder1.AddMessageIntegrityShortTerm("password"); builder1.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); handler.ProcessRequest(message); // we expect back a response with a valid message integrity field spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader1.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); ChkA(reader1.ValidateMessageIntegrityShort("password")); // ----------------------------------------------------------------------- // simulate a user with a bad password spBuffer.reset(); InitBindingRequest(builder2); builder2.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "WrongUser"); builder2.AddMessageIntegrityShortTerm("wrongpassword"); builder2.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); _spTransport->ClearStream(); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader2.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); errorcode = 0; ChkA(reader2.GetErrorCode(&errorcode)); ChkIfA(errorcode != ::STUN_ERROR_UNAUTHORIZED, E_FAIL); // ----------------------------------------------------------------------- // simulate a client sending no credentials - we expect it to fire back with a 400/bad-request spBuffer.reset(); InitBindingRequest(builder3); builder3.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); _spTransport->ClearStream(); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader3.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); errorcode = 0; ChkA(reader3.GetErrorCode(&errorcode)); ChkIfA(errorcode != ::STUN_ERROR_BADREQUEST, E_FAIL); Cleanup: return hr; }
// send a binding request to a duplex server instructing it to send back on it's alternate port and alternate IP to an alternate client port HRESULT CTestMessageHandler::Test2() { HRESULT hr=S_OK; CStunMessageBuilder builder; CSocketAddress clientaddr(0x12345678, 9876); CSocketAddress recvaddr; uint16_t responsePort = 2222; CRefCountedBuffer spBuffer; CStunThreadMessageHandler handler; CStunMessageReader reader; CStunMessageReader::ReaderParseState state; ::StunChangeRequestAttribute changereq; StunMessageEnvelope message; _spTransport->Reset(); _spTransport->AddPP(CSocketAddress(0xaaaaaaaa, 1234)); _spTransport->AddPA(CSocketAddress(0xaaaaaaaa, 1235)); _spTransport->AddAP(CSocketAddress(0xbbbbbbbb, 1234)); _spTransport->AddAA(CSocketAddress(0xbbbbbbbb, 1235)); InitBindingRequest(builder); builder.AddResponsePort(responsePort); changereq.fChangeIP = true; changereq.fChangePort = true; builder.AddChangeRequest(changereq); builder.AddResponsePort(responsePort); builder.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(RolePP, &(message.localAddr)); handler.SetResponder(_spTransport); handler.ProcessRequest(message); spBuffer->Reset(); _spTransport->GetOutputStream().GetBuffer(&spBuffer); // parse the response state = reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); // validate that the binding response matches our expectations ChkA(ValidateMappedAddress(reader, clientaddr)); ChkA(ValidateOriginAddress(reader, RoleAA)); // did it get sent back to where we thought it was recvaddr = clientaddr; recvaddr.SetPort(responsePort); ChkA(ValidateResponseAddress(recvaddr)); Cleanup: return hr; }
void axfr_fork(TASK *t) { int pfd[2] = { -1, -1 }; /* Parent/child pipe descriptors */ pid_t pid = -1, parent = -1; #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("%s: axfr_fork called on fd %d"), desctask(t), t->fd); #endif if (pipe(pfd)) Err(_("pipe")); parent = getpid(); if ((pid = fork()) < 0) { close(pfd[0]); close(pfd[1]); Warn(_("%s: fork"), clientaddr(t)); return; } if (!pid) { /* Child: reset all signal handlers to default before we dive off elsewhere */ struct sigaction act; memset(&act, 0, sizeof(act)); sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_DFL; sigaction(SIGHUP, &act, NULL); sigaction(SIGUSR1, &act, NULL); sigaction(SIGUSR2, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGQUIT, &act, NULL); sigaction(SIGABRT, &act, NULL); sigaction(SIGTERM, &act, NULL); #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("%s: axfr_fork is in the child"), desctask(t)); #endif /* Let parent know I have started */ close(pfd[0]); if (write(pfd[1], "OK", 2) != 2) Warn(_("error writing startup notification")); close(pfd[1]); #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("%s: axfr_fork child has told parent I am running"), desctask(t)); #endif /* Clean up parents resources */ free_other_tasks(t, 1); #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("%s: AXFR child built"), desctask(t)); #endif /* Do AXFR */ axfr(t); } else { /* Parent */ char buf[5] = "\0\0\0\0\0"; int errct = 0; close(pfd[1]); for (errct = 0; errct < 5; errct++) { if (read(pfd[0], &buf, 4) != 2) Warn(_("%s (%d of 5)"), _("error reading startup notification"), errct+1); else break; } close(pfd[0]); #if DEBUG_ENABLED && DEBUG_AXFR DebugX("axfr", 1,_("AXFR: process started on pid %d for TCP fd %d, task ID %u"), pid, t->fd, t->internal_id); #endif } /* NOTREACHED*/ }