/* Process M-SEARCH requests */ static void processMSEARCH(int s, const char * st, int st_len, const struct sockaddr * addr) { struct service * serv; #ifdef ENABLE_IPV6 char buf[64]; #endif if(!st || st_len==0) return; #ifdef ENABLE_IPV6 sockaddr_to_string(addr, buf, sizeof(buf)); syslog(LOG_INFO, "SSDP M-SEARCH from %s ST:%.*s", buf, st_len, st); #else syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s", inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr), ntohs(((const struct sockaddr_in *)addr)->sin_port), st_len, st); #endif /* TODO : ignore packet if not coming from a LAN */ if(st_len==8 && (0==memcmp(st, "ssdp:all", 8))) { /* send a response for all services */ for(serv = servicelisthead.lh_first; serv; serv = serv->entries.le_next) { SendSSDPMSEARCHResponse(s, addr, serv->st, serv->usn, serv->server, serv->location); } } else if(st_len > 5 && (0==memcmp(st, "uuid:", 5))) { /* find a matching UUID value */ for(serv = servicelisthead.lh_first; serv; serv = serv->entries.le_next) { if(0 == strncmp(serv->usn, st, st_len)) { SendSSDPMSEARCHResponse(s, addr, serv->st, serv->usn, serv->server, serv->location); } } } else { /* find matching services */ /* remove version at the end of the ST string */ if(st[st_len-2]==':' && isdigit(st[st_len-1])) st_len -= 2; /* answer for each matching service */ for(serv = servicelisthead.lh_first; serv; serv = serv->entries.le_next) { if(0 == strncmp(serv->st, st, st_len)) { SendSSDPMSEARCHResponse(s, addr, serv->st, serv->usn, serv->server, serv->location); } } } }
/** * Wraper for sockaddr_to_string() which never fails. */ static char * one_socket_to_string(const struct one_socket *s) { char *p = sockaddr_to_string(&s->address, s->address_length, NULL); if (p == NULL) p = g_strdup("[unknown]"); return p; }
char *pingu_route_to_string(struct pingu_route *route, char *buf, size_t bufsize) { char deststr[64] = "", gwstr[64] = "", viastr[68] = ""; char ifname[IF_NAMESIZE] = "", devstr[IF_NAMESIZE + 5] = ""; sockaddr_to_string(&route->dest, deststr, sizeof(deststr)); sockaddr_to_string(&route->gw_addr, gwstr, sizeof(gwstr)); if (gwstr[0] != '\0') snprintf(viastr, sizeof(viastr), "via %s ", gwstr); if (if_indextoname(route->dev_index, ifname) != NULL) snprintf(devstr, sizeof(devstr), "dev %s ", ifname); snprintf(buf, bufsize, "%s/%i %s%smetric %i", deststr, route->dst_len, viastr, devstr, route->metric); return buf; }
static void httpd_listen_in_event(int fd, const struct sockaddr *address, size_t address_length, G_GNUC_UNUSED int uid, void *ctx) { struct httpd_output *httpd = ctx; /* the listener socket has become readable - a client has connected */ #ifdef HAVE_LIBWRAP if (address->sa_family != AF_UNIX) { char *hostaddr = sockaddr_to_string(address, address_length, NULL); const char *progname = g_get_prgname(); struct request_info req; request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0); fromhost(&req); if (!hosts_access(&req)) { /* tcp wrappers says no */ g_warning("libwrap refused connection (libwrap=%s) from %s", progname, hostaddr); g_free(hostaddr); close_socket(fd); g_mutex_unlock(httpd->mutex); return; } g_free(hostaddr); } #else (void)address; (void)address_length; #endif /* HAVE_WRAP */ g_mutex_lock(httpd->mutex); if (fd >= 0) { /* can we allow additional client */ if (httpd->open && (httpd->clients_max == 0 || httpd->clients_cnt < httpd->clients_max)) httpd_client_add(httpd, fd); else close_socket(fd); } else if (fd < 0 && errno != EINTR) { g_warning("accept() failed: %s", g_strerror(errno)); } g_mutex_unlock(httpd->mutex); }
/* ==================== __tcp_accept ==================== */ I32 __tcp_accept(lua_State* ls) { U32 listen_sock = INVALID_SOCKET; U32 fd = INVALID_SOCKET; I32 opt = 0; I32 ret = -1; struct sockaddr_in sock_addr; I32 addr_len = sizeof(sock_addr); const CHAR* ip; LUA_STACK; LUA_WATCH(ls); // check the socket if(lua_gettop(ls) != 1 || lua_type(ls, 1) != LUA_TNUMBER) { GLog(LT_ERROR, "__tcp_accept : invalid socket!"); return 0; } listen_sock = (U32)lua_tointeger(ls, 1); CHECK(listen_sock != INVALID_SOCKET); // accept a new connection memset(&sock_addr, 0, sizeof(sock_addr)); fd = accept(listen_sock, (struct sockaddr*)&sock_addr, &addr_len); if(fd == INVALID_SOCKET) { if(WSAGetLastError() != WSAEWOULDBLOCK) { GLog(LT_ERROR, "__tcp_accept: There was an error accept a socket : %s\n", get_socket_error_string()); return 0; } return 0; } // make it non-blocking opt = 1; ret = ioctlsocket(fd, FIONBIO, (U32*)&opt); if(ret < 0) { GLog(LT_ERROR, "__tcp_accept: There was an error making a socket non-blocking : %s\n", get_socket_error_string()); return 0; } // return the socket and the ipaddr lua_pushinteger(ls, fd); LUA_WATCH(ls); ip = sockaddr_to_string(&sock_addr); lua_pushstring(ls, ip); LUA_WATCH(ls); return 2; }
/* not really an SSDP "announce" as it is the response * to a SSDP "M-SEARCH" */ static void SendSSDPAnnounce2(int s, const struct sockaddr * addr, const char * st, int st_len, const char * suffix, const char * host, unsigned short port) { int l, n; char buf[512]; char addr_str[64]; socklen_t addrlen; /* * follow guideline from document "UPnP Device Architecture 1.0" * uppercase is recommended. * DATE: is recommended * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0 * - check what to put in the 'Cache-Control' header * * have a look at the document "UPnP Device Architecture v1.1 */ l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n" "CACHE-CONTROL: max-age=120\r\n" /*"DATE: ...\r\n"*/ "ST: %.*s%s\r\n" "USN: %s::%.*s%s\r\n" "EXT:\r\n" "SERVER: " MINIUPNPD_SERVER_STRING "\r\n" "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n" "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */ "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */ "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ "\r\n", st_len, st, suffix, uuidvalue, st_len, st, suffix, host, (unsigned int)port, upnp_bootid, upnp_bootid, upnp_configid); addrlen = (addr->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); n = sendto(s, buf, l, 0, addr, addrlen); sockaddr_to_string(addr, addr_str, sizeof(addr_str)); syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n, addr_str, l, buf); if(n < 0) { /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ syslog(LOG_ERR, "sendto(udp): %m"); } }
void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid) { struct client *client; char *remote; if (num_clients >= client_max_connections) { g_warning("Max Connections Reached!"); close(fd); return; } client = g_new0(struct client, 1); clients = g_list_prepend(clients, client); ++num_clients; client_init(client, fd); client->uid = uid; remote = sockaddr_to_string(sa, sa_length, NULL); g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE, "[%u] opened from %s", client->num, remote); g_free(remote); }
int get_src_for_route_to(const struct sockaddr * dst, void * src, size_t * src_len, int * index) { int found = 0; int s; int l, i; char * p; struct sockaddr * sa; struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; #define rtm m_rtmsg.m_rtm if(dst == NULL) return -1; #ifdef __APPLE__ if(dst->sa_family == AF_INET6) { syslog(LOG_ERR, "Sorry, get_src_for_route_to() is known to fail with IPV6 on OS X..."); return -1; } #endif s = socket(PF_ROUTE, SOCK_RAW, dst->sa_family); if(s < 0) { syslog(LOG_ERR, "socket(PF_ROUTE) failed : %m"); return -1; } memset(&rtm, 0, sizeof(rtm)); rtm.rtm_type = RTM_GET; rtm.rtm_flags = RTF_UP; rtm.rtm_version = RTM_VERSION; rtm.rtm_seq = 1; rtm.rtm_addrs = RTA_DST; /* destination address */ memcpy(m_rtmsg.m_space, dst, sizeof(struct sockaddr)); rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr); if(write(s, &m_rtmsg, rtm.rtm_msglen) < 0) { syslog(LOG_ERR, "write: %m"); close(s); return -1; } do { l = read(s, &m_rtmsg, sizeof(m_rtmsg)); if(l<0) { syslog(LOG_ERR, "read: %m"); close(s); return -1; } syslog(LOG_DEBUG, "read l=%d seq=%d pid=%d", l, rtm.rtm_seq, rtm.rtm_pid); } while(l > 0 && (rtm.rtm_pid != getpid() || rtm.rtm_seq != 1)); close(s); p = m_rtmsg.m_space; if(rtm.rtm_addrs) { for(i=1; i<0x8000; i <<= 1) { if(i & rtm.rtm_addrs) { char tmp[256] = { 0 }; sa = (struct sockaddr *)p; sockaddr_to_string(sa, tmp, sizeof(tmp)); syslog(LOG_DEBUG, "type=%d sa_len=%d sa_family=%d %s", i, SA_LEN(sa), sa->sa_family, tmp); if((i == RTA_DST || i == RTA_GATEWAY) && (src_len && src)) { size_t len = 0; void * paddr = NULL; if(sa->sa_family == AF_INET) { paddr = &((struct sockaddr_in *)sa)->sin_addr; len = sizeof(struct in_addr); } else if(sa->sa_family == AF_INET6) { paddr = &((struct sockaddr_in6 *)sa)->sin6_addr; len = sizeof(struct in6_addr); } if(paddr) { if(*src_len < len) { syslog(LOG_WARNING, "cannot copy src. %u<%u", (unsigned)*src_len, (unsigned)len); return -1; } memcpy(src, paddr, len); *src_len = len; found = 1; } } #ifdef AF_LINK if(sa->sa_family == AF_LINK) { struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa; if(index) *index = sdl->sdl_index; } #endif p += SA_LEN(sa); } } } return found ? 0 : -1; }
struct lan_addr_s * get_lan_for_peer(const struct sockaddr * peer) { struct lan_addr_s * lan_addr = NULL; #ifdef DEBUG char dbg_str[64]; #endif /* DEBUG */ #ifdef ENABLE_IPV6 if(peer->sa_family == AF_INET6) { struct sockaddr_in6 * peer6 = (struct sockaddr_in6 *)peer; if(IN6_IS_ADDR_V4MAPPED(&peer6->sin6_addr)) { struct in_addr peer_addr; memcpy(&peer_addr, &peer6->sin6_addr.s6_addr[12], 4); for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if( (peer_addr.s_addr & lan_addr->mask.s_addr) == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) break; } } else { int index = -1; if(peer6->sin6_scope_id > 0) index = (int)peer6->sin6_scope_id; else { if(get_src_for_route_to(peer, NULL, NULL, &index) < 0) return NULL; } syslog(LOG_DEBUG, "%s looking for LAN interface index=%d", "get_lan_for_peer()", index); for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { syslog(LOG_DEBUG, "ifname=%s index=%u str=%s addr=%08x mask=%08x", lan_addr->ifname, lan_addr->index, lan_addr->str, ntohl(lan_addr->addr.s_addr), ntohl(lan_addr->mask.s_addr)); if(index == (int)lan_addr->index) break; } } } else if(peer->sa_family == AF_INET) { #endif /* ENABLE_IPV6 */ for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if( (((const struct sockaddr_in *)peer)->sin_addr.s_addr & lan_addr->mask.s_addr) == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) break; } #ifdef ENABLE_IPV6 } #endif /* ENABLE_IPV6 */ #ifdef DEBUG sockaddr_to_string(peer, dbg_str, sizeof(dbg_str)); if(lan_addr) { syslog(LOG_DEBUG, "%s: %s found in LAN %s %s", "get_lan_for_peer()", dbg_str, lan_addr->ifname, lan_addr->str); } else { syslog(LOG_DEBUG, "%s: %s not found !", "get_lan_for_peer()", dbg_str); } #endif /* DEBUG */ return lan_addr; }
/** * Process the message and add/drop multicast membership if needed */ int ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6) { struct lan_addr_s * lan_addr; ssize_t len; char buffer[4096]; #ifdef __linux__ struct iovec iov; struct msghdr hdr; struct nlmsghdr *nlhdr; struct ifaddrmsg *ifa; struct rtattr *rta; int ifa_len; iov.iov_base = buffer; iov.iov_len = sizeof(buffer); memset(&hdr, 0, sizeof(hdr)); hdr.msg_iov = &iov; hdr.msg_iovlen = 1; len = recvmsg(s, &hdr, 0); if(len < 0) { syslog(LOG_ERR, "recvmsg(s, &hdr, 0): %m"); return -1; } for(nlhdr = (struct nlmsghdr *)buffer; NLMSG_OK(nlhdr, len); nlhdr = NLMSG_NEXT(nlhdr, len)) { int is_del = 0; char address[48]; char ifname[IFNAMSIZ]; address[0] = '\0'; ifname[0] = '\0'; if(nlhdr->nlmsg_type == NLMSG_DONE) break; switch(nlhdr->nlmsg_type) { /* case RTM_NEWLINK: */ /* case RTM_DELLINK: */ case RTM_DELADDR: is_del = 1; case RTM_NEWADDR: /* http://linux-hacks.blogspot.fr/2009/01/sample-code-to-learn-netlink.html */ ifa = (struct ifaddrmsg *)NLMSG_DATA(nlhdr); rta = (struct rtattr *)IFA_RTA(ifa); ifa_len = IFA_PAYLOAD(nlhdr); syslog(LOG_DEBUG, "%s %s index=%d fam=%d prefixlen=%d flags=%d scope=%d", "ProcessInterfaceWatchNotify", is_del ? "RTM_DELADDR" : "RTM_NEWADDR", ifa->ifa_index, ifa->ifa_family, ifa->ifa_prefixlen, ifa->ifa_flags, ifa->ifa_scope); for(;RTA_OK(rta, ifa_len); rta = RTA_NEXT(rta, ifa_len)) { /*RTA_DATA(rta)*/ /*rta_type : IFA_ADDRESS, IFA_LOCAL, etc. */ char tmp[128]; memset(tmp, 0, sizeof(tmp)); switch(rta->rta_type) { case IFA_ADDRESS: case IFA_LOCAL: case IFA_BROADCAST: case IFA_ANYCAST: inet_ntop(ifa->ifa_family, RTA_DATA(rta), tmp, sizeof(tmp)); if(rta->rta_type == IFA_ADDRESS) strncpy(address, tmp, sizeof(address)); break; case IFA_LABEL: strncpy(tmp, RTA_DATA(rta), sizeof(tmp)); strncpy(ifname, tmp, sizeof(ifname)); break; case IFA_CACHEINFO: { struct ifa_cacheinfo *cache_info; cache_info = RTA_DATA(rta); snprintf(tmp, sizeof(tmp), "valid=%u prefered=%u", cache_info->ifa_valid, cache_info->ifa_prefered); } break; default: strncpy(tmp, "*unknown*", sizeof(tmp)); } syslog(LOG_DEBUG, " rta_len=%d rta_type=%d '%s'", rta->rta_len, rta->rta_type, tmp); } syslog(LOG_INFO, "%s: %s/%d %s", is_del ? "RTM_DELADDR" : "RTM_NEWADDR", address, ifa->ifa_prefixlen, ifname); for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if((0 == strcmp(address, lan_addr->str)) || (0 == strcmp(ifname, lan_addr->ifname)) || (ifa->ifa_index == lan_addr->index)) { if(ifa->ifa_family == AF_INET) AddDropMulticastMembership(s_ssdp, lan_addr, 0, is_del); else if(ifa->ifa_family == AF_INET6) AddDropMulticastMembership(s_ssdp6, lan_addr, 1, is_del); break; } } break; default: syslog(LOG_DEBUG, "unknown nlmsg_type=%d", nlhdr->nlmsg_type); } } #else /* __linux__ */ struct rt_msghdr * rtm; struct ifa_msghdr * ifam; int is_del = 0; char tmp[64]; char * p; struct sockaddr * sa; int addr; char address[48]; char ifname[IFNAMSIZ]; int family = AF_UNSPEC; int prefixlen = 0; address[0] = '\0'; ifname[0] = '\0'; len = recv(s, buffer, sizeof(buffer), 0); if(len < 0) { syslog(LOG_ERR, "%s recv: %m", "ProcessInterfaceWatchNotify"); return -1; } rtm = (struct rt_msghdr *)buffer; switch(rtm->rtm_type) { case RTM_DELADDR: is_del = 1; case RTM_NEWADDR: ifam = (struct ifa_msghdr *)buffer; syslog(LOG_DEBUG, "%s %s len=%d/%hu index=%hu addrs=%x flags=%x", "ProcessInterfaceWatchNotify", is_del?"RTM_DELADDR":"RTM_NEWADDR", (int)len, ifam->ifam_msglen, ifam->ifam_index, ifam->ifam_addrs, ifam->ifam_flags); p = buffer + sizeof(struct ifa_msghdr); addr = 1; while(p < buffer + len) { sa = (struct sockaddr *)p; while(!(addr & ifam->ifam_addrs) && (addr <= ifam->ifam_addrs)) addr = addr << 1; sockaddr_to_string(sa, tmp, sizeof(tmp)); syslog(LOG_DEBUG, " %s", tmp); switch(addr) { case RTA_DST: case RTA_GATEWAY: break; case RTA_NETMASK: if(sa->sa_family == AF_INET #if defined(__OpenBSD__) || (sa->sa_family == 0 && sa->sa_len <= sizeof(struct sockaddr_in)) #endif ) { uint32_t sin_addr = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); while((prefixlen < 32) && ((sin_addr & (1 << (31 - prefixlen))) != 0)) prefixlen++; } else if(sa->sa_family == AF_INET6 #if defined(__OpenBSD__) || (sa->sa_family == 0 && sa->sa_len == sizeof(struct sockaddr_in6)) #endif ) { int i = 0; uint8_t * q = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr; while((*q == 0xff) && (i < 16)) { prefixlen += 8; q++; i++; } if(i < 16) { i = 0; while((i < 8) && ((*q & (1 << (7 - i))) != 0)) i++; prefixlen += i; } } break; case RTA_GENMASK: break; case RTA_IFP: #ifdef AF_LINK if(sa->sa_family == AF_LINK) { struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa; memset(ifname, 0, sizeof(ifname)); memcpy(ifname, sdl->sdl_data, sdl->sdl_nlen); } #endif break; case RTA_IFA: family = sa->sa_family; if(sa->sa_family == AF_INET) { inet_ntop(sa->sa_family, &((struct sockaddr_in *)sa)->sin_addr, address, sizeof(address)); } else if(sa->sa_family == AF_INET6) { inet_ntop(sa->sa_family, &((struct sockaddr_in6 *)sa)->sin6_addr, address, sizeof(address)); } break; case RTA_AUTHOR: break; case RTA_BRD: break; } #if 0 syslog(LOG_DEBUG, " %d.%d.%d.%d %02x%02x%02x%02x", (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2], (uint8_t)p[3], (uint8_t)p[0], (uint8_t)p[1], (uint8_t)p[2], (uint8_t)p[3]); syslog(LOG_DEBUG, " %d.%d.%d.%d %02x%02x%02x%02x", (uint8_t)p[4], (uint8_t)p[5], (uint8_t)p[6], (uint8_t)p[7], (uint8_t)p[4], (uint8_t)p[5], (uint8_t)p[6], (uint8_t)p[7]); #endif p += SA_RLEN(sa); addr = addr << 1; } syslog(LOG_INFO, "%s: %s/%d %s", is_del ? "RTM_DELADDR" : "RTM_NEWADDR", address, prefixlen, ifname); for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if((0 == strcmp(address, lan_addr->str)) || (0 == strcmp(ifname, lan_addr->ifname)) || (ifam->ifam_index == lan_addr->index)) { if(family == AF_INET) AddDropMulticastMembership(s_ssdp, lan_addr, 0, is_del); else if(family == AF_INET6) AddDropMulticastMembership(s_ssdp6, lan_addr, 1, is_del); break; } } break; default: syslog(LOG_DEBUG, "Unknown RTM message : rtm->rtm_type=%d len=%d", rtm->rtm_type, (int)len); } #endif return 0; }
int qtrun(struct qtproto* p) { if (getconf("DEBUG")) debug = 1; struct qtsession session; session.poll_timeout = -1; session.protocol = *p; if (init_udp(&session) < 0) return -1; int sfd = session.fd_socket; session.sendnetworkpacket = qtsendnetworkpacket; if (init_tuntap(&session) < 0) return -1; int ttfd = session.fd_dev; char protocol_data[p->protocol_data_size]; memset(protocol_data, 0, p->protocol_data_size); session.protocol_data = &protocol_data; if (p->init && p->init(&session) < 0) return -1; if (drop_privileges() < 0) return -1; fprintf(stderr, "The tunnel is now operational!\n"); struct pollfd fds[2]; fds[0].fd = ttfd; fds[0].events = POLLIN; fds[1].fd = sfd; fds[1].events = POLLIN; int pi_length = 0; if (session.use_pi == 2) pi_length = 4; char buffer_raw_a[p->buffersize_raw + pi_length]; char buffer_enc_a[p->buffersize_enc]; char* buffer_raw = buffer_raw_a; char* buffer_enc = buffer_enc_a; while (1) { int len = poll(fds, 2, session.poll_timeout); if (len < 0) return errorexitp("poll error"); else if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) return errorexit("poll error on tap device"); else if (fds[1].revents & (POLLHUP | POLLNVAL)) return errorexit("poll error on udp socket"); if (len == 0 && p->idle) p->idle(&session); if (fds[0].revents & POLLIN) { len = read(ttfd, buffer_raw + p->offset_raw, p->buffersize_raw + pi_length); if (len < pi_length) errorexit("read packet smaller than header from tun device"); if (session.remote_float == 0 || session.remote_float == 2) { len = p->encode(&session, buffer_raw + pi_length, buffer_enc, len - pi_length); if (len < 0) return len; if (len == 0) continue; //encoding is not yet possible qtsendnetworkpacket(&session, buffer_enc + p->offset_enc, len); } } if (fds[1].revents & POLLERR) { int out; len = sizeof(out); getsockopt(sfd, SOL_SOCKET, SO_ERROR, &out, &len); fprintf(stderr, "Received error %d on udp socket\n", out); } if (fds[1].revents & POLLIN) { sockaddr_any recvaddr; socklen_t recvaddr_len = sizeof(recvaddr); if (session.remote_float == 0) { len = read(sfd, buffer_enc + p->offset_enc, p->buffersize_enc); } else { len = recvfrom(sfd, buffer_enc + p->offset_enc, p->buffersize_enc, 0, (struct sockaddr*)&recvaddr, &recvaddr_len); } if (len < 0) { long long out; len = sizeof(out); getsockopt(sfd, SOL_SOCKET, SO_ERROR, &out, &len); fprintf(stderr, "Received end of file on udp socket (error %lld)\n", out); } else { len = p->decode(&session, buffer_enc, buffer_raw + pi_length, len); if (len < 0) continue; if (session.remote_float != 0 && !sockaddr_equal(&session.remote_addr, &recvaddr)) { char epname[INET6_ADDRSTRLEN + 1 + 2 + 1 + 5]; //addr%scope:port sockaddr_to_string(&recvaddr, epname, sizeof(epname)); fprintf(stderr, "Remote endpoint has changed to %s\n", epname); session.remote_addr = recvaddr; session.remote_float = 2; } if (len > 0 && session.use_pi == 2) { int ipver = (buffer_raw[p->offset_raw + pi_length] >> 4) & 0xf; int pihdr = 0; #if defined linux if (ipver == 4) pihdr = 0x0000 | (0x0008 << 16); //little endian: flags and protocol are swapped else if (ipver == 6) pihdr = 0x0000 | (0xdd86 << 16); #else if (ipver == 4) pihdr = htonl(AF_INET); else if (ipver == 6) pihdr = htonl(AF_INET6); #endif *(int*)(buffer_raw + p->offset_raw) = pihdr; } if (len > 0) write(ttfd, buffer_raw + p->offset_raw, len + pi_length); } }
int get_src_for_route_to(const struct sockaddr * dst, void * src, size_t * src_len, int * index) { int found = 0; int s; int l, i; char * p; struct sockaddr * sa; struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; #define rtm m_rtmsg.m_rtm if(dst == NULL) return -1; if(dst->sa_len > 0) { l = dst->sa_len; } else { if(dst->sa_family == AF_INET) l = sizeof(struct sockaddr_in); else if(dst->sa_family == AF_INET6) l = sizeof(struct sockaddr_in6); else { syslog(LOG_ERR, "unknown address family %d", dst->sa_family); return -1; } } s = socket(PF_ROUTE, SOCK_RAW, dst->sa_family); if(s < 0) { syslog(LOG_ERR, "socket(PF_ROUTE) failed : %m"); return -1; } memset(&rtm, 0, sizeof(rtm)); rtm.rtm_type = RTM_GET; rtm.rtm_flags = RTF_UP; rtm.rtm_version = RTM_VERSION; rtm.rtm_seq = 1; rtm.rtm_addrs = RTA_DST | RTA_IFA | RTA_IFP; /* pass destination address, request source address & interface */ memcpy(m_rtmsg.m_space, dst, l); ((struct sockaddr *)m_rtmsg.m_space)->sa_len = l; rtm.rtm_msglen = sizeof(struct rt_msghdr) + l; if(write(s, &m_rtmsg, rtm.rtm_msglen) < 0) { syslog(LOG_ERR, "write: %m"); close(s); return -1; } do { l = read(s, &m_rtmsg, sizeof(m_rtmsg)); if(l<0) { syslog(LOG_ERR, "read: %m"); close(s); return -1; } syslog(LOG_DEBUG, "read l=%d seq=%d pid=%d sizeof(struct rt_msghdr)=%d", l, rtm.rtm_seq, rtm.rtm_pid, (int)sizeof(struct rt_msghdr)); } while(l > 0 && (rtm.rtm_pid != getpid() || rtm.rtm_seq != 1)); close(s); if(l <= 0) { syslog(LOG_WARNING, "no matching ROUTE response message"); return -1; } p = m_rtmsg.m_space; if(rtm.rtm_addrs) { for(i=1; i<0x8000; i <<= 1) { if(i & rtm.rtm_addrs) { char tmp[256] = { 0 }; if(p >= (char *)&m_rtmsg + l) { syslog(LOG_ERR, "error parsing ROUTE response message"); break; } sa = (struct sockaddr *)p; sockaddr_to_string(sa, tmp, sizeof(tmp)); syslog(LOG_DEBUG, "type=%d sa_len=%d sa_family=%d %s", i, sa->sa_len, sa->sa_family, tmp); if(i == RTA_IFA) { size_t len = 0; void * paddr = NULL; if(sa->sa_family == AF_INET) { paddr = &((struct sockaddr_in *)sa)->sin_addr; len = sizeof(struct in_addr); } else if(sa->sa_family == AF_INET6) { paddr = &((struct sockaddr_in6 *)sa)->sin6_addr; len = sizeof(struct in6_addr); } if(paddr) { if(src && src_len) { if(*src_len < len) { syslog(LOG_WARNING, "cannot copy src. %u<%u", (unsigned)*src_len, (unsigned)len); return -1; } memcpy(src, paddr, len); *src_len = len; } found = 1; } } #ifdef AF_LINK else if((i == RTA_IFP) && (sa->sa_family == AF_LINK)) { struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa; if(index) *index = sdl->sdl_index; } #endif /* at least 4 bytes per address are reserved, * that is true with OpenBSD 4.3 */ if(SA_SIZE(sa) > 0) p += SA_SIZE(sa); else p += 4; } } } return found ? 0 : -1; }
void ProcessSSDPData(int s, const char *bufr, int n, const struct sockaddr * sender, unsigned short http_port) #endif { int i, l; struct lan_addr_s * lan_addr = NULL; const char * st = NULL; int st_len = 0; int st_ver = 0; char sender_str[64]; char ver_str[4]; const char * announced_host = NULL; #ifdef UPNP_STRICT #ifdef ENABLE_IPV6 char announced_host_buf[64]; #endif #endif #if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE) int mx_value = -1; #endif unsigned int delay = 50; /* Non-zero default delay to prevent flooding */ /* UPnP Device Architecture v1.1. 1.3.3 Search response : * Devices responding to a multicast M-SEARCH SHOULD wait a random period * of time between 0 seconds and the number of seconds specified in the * MX field value of the search request before responding, in order to * avoid flooding the requesting control point with search responses * from multiple devices. If the search request results in the need for * a multiple part response from the device, those multiple part * responses SHOULD be spread at random intervals through the time period * from 0 to the number of seconds specified in the MX header field. */ /* get the string representation of the sender address */ sockaddr_to_string(sender, sender_str, sizeof(sender_str)); lan_addr = get_lan_for_peer(sender); if(lan_addr == NULL) { syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring", sender_str); return; } if(memcmp(bufr, "NOTIFY", 6) == 0) { /* ignore NOTIFY packets. We could log the sender and device type */ return; } else if(memcmp(bufr, "M-SEARCH", 8) == 0) { i = 0; while(i < n) { while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n')) i++; i += 2; if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0)) { st = bufr+i+3; st_len = 0; while((*st == ' ' || *st == '\t') && (st < bufr + n)) st++; while(st[st_len]!='\r' && st[st_len]!='\n' && (st + st_len < bufr + n)) st_len++; l = st_len; while(l > 0 && st[l-1] != ':') l--; st_ver = atoi(st+l); syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver); /*j = 0;*/ /*while(bufr[i+j]!='\r') j++;*/ /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/ } #if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE) else if((i < n - 3) && (strncasecmp(bufr+i, "mx:", 3) == 0)) { const char * mx; int mx_len; mx = bufr+i+3; mx_len = 0; while((*mx == ' ' || *mx == '\t') && (mx < bufr + n)) mx++; while(mx[mx_len]!='\r' && mx[mx_len]!='\n' && (mx + mx_len < bufr + n)) mx_len++; mx_value = atoi(mx); syslog(LOG_DEBUG, "MX: %.*s (value=%d)", mx_len, mx, mx_value); } #endif } #ifdef UPNP_STRICT /* For multicast M-SEARCH requests, if the search request does * not contain an MX header field, the device MUST silently * discard and ignore the search request. */ if(mx_value < 0) { syslog(LOG_INFO, "ignoring SSDP packet missing MX: header"); return; } else if(mx_value > 5) { /* If the MX header field specifies a field value greater * than 5, the device SHOULD assume that it contained the * value 5 or less. */ mx_value = 5; } #elif defined(DELAY_MSEARCH_RESPONSE) if(mx_value < 0) { mx_value = 1; } else if(mx_value > 5) { /* If the MX header field specifies a field value greater * than 5, the device SHOULD assume that it contained the * value 5 or less. */ mx_value = 5; } #endif /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s", sender_str );*/ if(st && (st_len > 0)) { syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s", sender_str, st_len, st); /* find in which sub network the client is */ if(sender->sa_family == AF_INET) { if (lan_addr == NULL) { syslog(LOG_ERR, "Can't find in which sub network the client is"); return; } announced_host = lan_addr->str; } #ifdef ENABLE_IPV6 else { /* IPv6 address with brackets */ #ifdef UPNP_STRICT int index; struct in6_addr addr6; size_t addr6_len = sizeof(addr6); /* retrieve the IPv6 address which * will be used locally to reach sender */ memset(&addr6, 0, sizeof(addr6)); if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) { syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets); announced_host = ipv6_addr_for_http_with_brackets; } else { if(inet_ntop(AF_INET6, &addr6, announced_host_buf+1, sizeof(announced_host_buf) - 2)) { announced_host_buf[0] = '['; i = strlen(announced_host_buf); if(i < (int)sizeof(announced_host_buf) - 1) { announced_host_buf[i] = ']'; announced_host_buf[i+1] = '\0'; } else { syslog(LOG_NOTICE, "cannot suffix %s with ']'", announced_host_buf); } announced_host = announced_host_buf; } else { syslog(LOG_NOTICE, "inet_ntop() failed %m"); announced_host = ipv6_addr_for_http_with_brackets; } } #else announced_host = ipv6_addr_for_http_with_brackets; #endif } #endif /* Responds to request with a device as ST header */ for(i = 0; known_service_types[i].s; i++) { l = (int)strlen(known_service_types[i].s); if(l<=st_len && (0 == memcmp(st, known_service_types[i].s, l)) #ifdef UPNP_STRICT && (st_ver <= known_service_types[i].version) /* only answer for service version lower or equal of supported one */ #endif ) { /* SSDP_RESPOND_SAME_VERSION : * response is urn:schemas-upnp-org:service:WANIPConnection:1 when * M-SEARCH included urn:schemas-upnp-org:service:WANIPConnection:1 * else the implemented versions is included in the response * * From UPnP Device Architecture v1.1 : * 1.3.2 [...] Updated versions of device and service types * are REQUIRED to be fully backward compatible with * previous versions. Devices MUST respond to M-SEARCH * requests for any supported version. For example, if a * device implements “urn:schemas-upnporg:service:xyz:2”, * it MUST respond to search requests for both that type * and “urn:schemas-upnp-org:service:xyz:1”. The response * MUST specify the same version as was contained in the * search request. [...] */ #ifndef SSDP_RESPOND_SAME_VERSION if(i==0) ver_str[0] = '\0'; else snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version); #endif syslog(LOG_INFO, "Single search found"); #ifdef DELAY_MSEARCH_RESPONSE delay = random() / (1 + RAND_MAX / (1000 * mx_value)); #ifdef DEBUG syslog(LOG_DEBUG, "mx=%dsec delay=%ums", mx_value, delay); #endif #endif SendSSDPResponse(s, sender, #ifdef SSDP_RESPOND_SAME_VERSION st, st_len, "", #else known_service_types[i].s, l, ver_str, #endif announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif known_service_types[i].uuid, delay); break; } } /* Responds to request with ST: ssdp:all */ /* strlen("ssdp:all") == 8 */ if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) { #ifdef DELAY_MSEARCH_RESPONSE unsigned int delay_increment = (mx_value * 1000) / 15; #endif syslog(LOG_INFO, "ssdp:all found"); for(i=0; known_service_types[i].s; i++) { #ifdef DELAY_MSEARCH_RESPONSE delay += delay_increment; #endif if(i==0) ver_str[0] = '\0'; else snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version); l = (int)strlen(known_service_types[i].s); SendSSDPResponse(s, sender, known_service_types[i].s, l, ver_str, announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif known_service_types[i].uuid, delay); } /* also answer for uuid */ #ifdef DELAY_MSEARCH_RESPONSE delay += delay_increment; #endif SendSSDPResponse(s, sender, uuidvalue_igd, strlen(uuidvalue_igd), "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_igd, delay); #ifdef DELAY_MSEARCH_RESPONSE delay += delay_increment; #endif SendSSDPResponse(s, sender, uuidvalue_wan, strlen(uuidvalue_wan), "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_wan, delay); #ifdef DELAY_MSEARCH_RESPONSE delay += delay_increment; #endif SendSSDPResponse(s, sender, uuidvalue_wcd, strlen(uuidvalue_wcd), "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_wcd, delay); } /* responds to request by UUID value */ l = (int)strlen(uuidvalue_igd); if(l==st_len) { #ifdef DELAY_MSEARCH_RESPONSE delay = random() / (1 + RAND_MAX / (1000 * mx_value)); #endif if(0 == memcmp(st, uuidvalue_igd, l)) { syslog(LOG_INFO, "ssdp:uuid (IGD) found"); SendSSDPResponse(s, sender, st, st_len, "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_igd, delay); } else if(0 == memcmp(st, uuidvalue_wan, l)) { syslog(LOG_INFO, "ssdp:uuid (WAN) found"); SendSSDPResponse(s, sender, st, st_len, "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_wan, delay); } else if(0 == memcmp(st, uuidvalue_wcd, l)) { syslog(LOG_INFO, "ssdp:uuid (WCD) found"); SendSSDPResponse(s, sender, st, st_len, "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_wcd, delay); } } } else { syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str); } } else { syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str); } }
void ProcessInterfaceWatchNotify(int s) { char buf[4096]; ssize_t len; char tmp[64]; struct rt_msghdr * rtm; struct if_msghdr * ifm; struct ifa_msghdr * ifam; #ifdef RTM_IFANNOUNCE struct if_announcemsghdr * ifanm; #endif char * p; struct sockaddr * sa; unsigned int ext_if_name_index = 0; len = recv(s, buf, sizeof(buf), 0); if(len < 0) { syslog(LOG_ERR, "ProcessInterfaceWatchNotify recv: %m"); return; } if(ext_if_name) { ext_if_name_index = if_nametoindex(ext_if_name); } rtm = (struct rt_msghdr *)buf; syslog(LOG_DEBUG, "%u rt_msg : msglen=%d version=%d type=%d", (unsigned)len, rtm->rtm_msglen, rtm->rtm_version, rtm->rtm_type); switch(rtm->rtm_type) { case RTM_IFINFO: /* iface going up/down etc. */ ifm = (struct if_msghdr *)buf; syslog(LOG_DEBUG, " RTM_IFINFO: addrs=%x flags=%x index=%hu", ifm->ifm_addrs, ifm->ifm_flags, ifm->ifm_index); break; case RTM_ADD: /* Add Route */ syslog(LOG_DEBUG, " RTM_ADD"); break; case RTM_DELETE: /* Delete Route */ syslog(LOG_DEBUG, " RTM_DELETE"); break; case RTM_CHANGE: /* Change Metrics or flags */ syslog(LOG_DEBUG, " RTM_CHANGE"); break; case RTM_GET: /* Report Metrics */ syslog(LOG_DEBUG, " RTM_GET"); break; #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: /* iface arrival/departure */ ifanm = (struct if_announcemsghdr *)buf; syslog(LOG_DEBUG, " RTM_IFANNOUNCE: index=%hu what=%hu ifname=%s", ifanm->ifan_index, ifanm->ifan_what, ifanm->ifan_name); break; #endif #ifdef RTM_IEEE80211 case RTM_IEEE80211: /* IEEE80211 wireless event */ syslog(LOG_DEBUG, " RTM_IEEE80211"); break; #endif case RTM_NEWADDR: /* address being added to iface */ ifam = (struct ifa_msghdr *)buf; syslog(LOG_DEBUG, " RTM_NEWADDR: addrs=%x flags=%x index=%hu", ifam->ifam_addrs, ifam->ifam_flags, ifam->ifam_index); p = buf + sizeof(struct ifa_msghdr); while(p < buf + len) { sa = (struct sockaddr *)p; sockaddr_to_string(sa, tmp, sizeof(tmp)); syslog(LOG_DEBUG, " %s", tmp); p += SA_RLEN(sa); } if(ifam->ifam_index == ext_if_name_index) { should_send_public_address_change_notif = 1; } break; case RTM_DELADDR: /* address being removed from iface */ ifam = (struct ifa_msghdr *)buf; if(ifam->ifam_index == ext_if_name_index) { should_send_public_address_change_notif = 1; } break; default: syslog(LOG_DEBUG, "unprocessed RTM message type=%d", rtm->rtm_type); } }
int get_src_for_route_to(const struct sockaddr * dst, void * src, size_t * src_len, int * index) { #if __linux__ int fd = -1; struct nlmsghdr *h; int status; struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = (void*) &req.n, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; const struct sockaddr_in * dst4; const struct sockaddr_in6 * dst6; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_GETROUTE; req.r.rtm_family = dst->sa_family; req.r.rtm_table = 0; req.r.rtm_protocol = 0; req.r.rtm_scope = 0; req.r.rtm_type = 0; req.r.rtm_src_len = 0; req.r.rtm_dst_len = 0; req.r.rtm_tos = 0; { char dst_str[128]; sockaddr_to_string(dst, dst_str, sizeof(dst_str)); syslog(LOG_DEBUG, "get_src_for_route_to (%s)", dst_str); } /* add address */ if(dst->sa_family == AF_INET) { dst4 = (const struct sockaddr_in *)dst; nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst4->sin_addr, 4); req.r.rtm_dst_len = 32; } else { dst6 = (const struct sockaddr_in6 *)dst; nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst6->sin6_addr, 16); req.r.rtm_dst_len = 128; } fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { syslog(LOG_ERR, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m"); return -1; } memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; req.n.nlmsg_seq = 1; iov.iov_len = req.n.nlmsg_len; status = sendmsg(fd, &msg, 0); if (status < 0) { syslog(LOG_ERR, "sendmsg(rtnetlink) : %m"); goto error; } memset(&req, 0, sizeof(req)); for(;;) { iov.iov_len = sizeof(req); status = recvmsg(fd, &msg, 0); if(status < 0) { if (errno == EINTR || errno == EAGAIN) continue; syslog(LOG_ERR, "recvmsg(rtnetlink) %m"); goto error; } if(status == 0) { syslog(LOG_ERR, "recvmsg(rtnetlink) EOF"); goto error; } for (h = (struct nlmsghdr*)&req.n; status >= (int)sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); if (l<0 || len>status) { if (msg.msg_flags & MSG_TRUNC) { syslog(LOG_ERR, "Truncated message"); } syslog(LOG_ERR, "malformed message: len=%d", len); goto error; } if(nladdr.nl_pid != 0 || h->nlmsg_seq != 1/*seq*/) { syslog(LOG_ERR, "wrong seq = %d\n", h->nlmsg_seq); /* Don't forget to skip that message. */ status -= NLMSG_ALIGN(len); h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); continue; } if(h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); syslog(LOG_ERR, "NLMSG_ERROR %d : %s", err->error, strerror(-err->error)); goto error; } if(h->nlmsg_type == RTM_NEWROUTE) { struct rtattr * rta; int len = h->nlmsg_len; len -= NLMSG_LENGTH(sizeof(struct rtmsg)); for(rta = RTM_RTA(NLMSG_DATA((h))); RTA_OK(rta, len); rta = RTA_NEXT(rta,len)) { unsigned char * data = RTA_DATA(rta); if(rta->rta_type == RTA_PREFSRC) { if(src_len && src) { if(*src_len < RTA_PAYLOAD(rta)) { syslog(LOG_WARNING, "cannot copy src: %u<%lu", (unsigned)*src_len, (unsigned long)RTA_PAYLOAD(rta)); goto error; } *src_len = RTA_PAYLOAD(rta); memcpy(src, data, RTA_PAYLOAD(rta)); } } else if(rta->rta_type == RTA_OIF) { if(index) memcpy(index, data, sizeof(int)); } } close(fd); return 0; } status -= NLMSG_ALIGN(len); h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); } } syslog(LOG_WARNING, "get_src_for_route_to() : src not found"); error: if(fd >= 0) close(fd); return -1; #else /* __linux__ */ int found = 0; int s; int l, i; char * p; struct sockaddr * sa; struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; #define rtm m_rtmsg.m_rtm if(dst == NULL) return -1; #ifdef __APPLE__ if(dst->sa_family == AF_INET6) { syslog(LOG_ERR, "Sorry, get_src_for_route_to() is known to fail with IPV6 on OS X..."); return -1; } #endif s = socket(PF_ROUTE, SOCK_RAW, dst->sa_family); if(s < 0) { syslog(LOG_ERR, "socket(PF_ROUTE) failed : %m"); return -1; } memset(&rtm, 0, sizeof(rtm)); rtm.rtm_type = RTM_GET; rtm.rtm_flags = RTF_UP; rtm.rtm_version = RTM_VERSION; rtm.rtm_seq = 1; rtm.rtm_addrs = RTA_DST; /* destination address */ memcpy(m_rtmsg.m_space, dst, sizeof(struct sockaddr)); rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr); if(write(s, &m_rtmsg, rtm.rtm_msglen) < 0) { syslog(LOG_ERR, "write: %m"); close(s); return -1; } do { l = read(s, &m_rtmsg, sizeof(m_rtmsg)); if(l<0) { syslog(LOG_ERR, "read: %m"); close(s); return -1; } syslog(LOG_DEBUG, "read l=%d seq=%d pid=%d", l, rtm.rtm_seq, rtm.rtm_pid); } while(l > 0 && (rtm.rtm_pid != getpid() || rtm.rtm_seq != 1)); close(s); p = m_rtmsg.m_space; if(rtm.rtm_addrs) { for(i=1; i<0x8000; i <<= 1) { if(i & rtm.rtm_addrs) { char tmp[256] = { 0 }; sa = (struct sockaddr *)p; sockaddr_to_string(sa, tmp, sizeof(tmp)); syslog(LOG_DEBUG, "type=%d sa_len=%d sa_family=%d %s", i, SA_LEN(sa), sa->sa_family, tmp); if((i == RTA_DST || i == RTA_GATEWAY) && (src_len && src)) { size_t len = 0; void * paddr = NULL; if(sa->sa_family == AF_INET) { paddr = &((struct sockaddr_in *)sa)->sin_addr; len = sizeof(struct in_addr); } else if(sa->sa_family == AF_INET6) { paddr = &((struct sockaddr_in6 *)sa)->sin6_addr; len = sizeof(struct in6_addr); } if(paddr) { if(*src_len < len) { syslog(LOG_WARNING, "cannot copy src. %u<%u", (unsigned)*src_len, (unsigned)len); return -1; } memcpy(src, paddr, len); *src_len = len; found = 1; } } #ifdef AF_LINK if(sa->sa_family == AF_LINK) { struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa; if(index) *index = sdl->sdl_index; } #endif p += SA_LEN(sa); } } } return found ? 0 : -1; #endif /* __linux__ */ }
int main(int argc, char ** argv) { struct sockaddr_in dst4; struct sockaddr_in6 dst6; struct sockaddr * dst; void * src; size_t src_len; int r; int index = -1; memset(&dst4, 0, sizeof(dst4)); memset(&dst6, 0, sizeof(dst6)); dst = NULL; if(argc < 2) { fprintf(stderr, "usage: %s <ip address>\n", argv[0]); fprintf(stderr, "both v4 and v6 IP addresses are supported.\n"); return 1; } openlog("testgetroute", LOG_CONS|LOG_PERROR, LOG_USER); r = inet_pton (AF_INET, argv[1], &dst4.sin_addr); if(r < 0) { syslog(LOG_ERR, "inet_pton(AF_INET, %s) : %m", argv[1]); closelog(); return 2; } if (r == 0) { r = inet_pton (AF_INET6, argv[1], &dst6.sin6_addr); if(r < 0) { syslog(LOG_ERR, "inet_pton(AF_INET6, %s) : %m", argv[1]); closelog(); return 2; } else if(r > 0) { dst6.sin6_family = AF_INET6; dst = (struct sockaddr *)&dst6; src = &dst6.sin6_addr; src_len = sizeof(dst6.sin6_addr); } else { /* r == 0 */ syslog(LOG_ERR, "%s is not a correct IPv4 or IPv6 address", argv[1]); closelog(); return 1; } } else { dst4.sin_family = AF_INET; dst = (struct sockaddr *)&dst4; src = &dst4.sin_addr; src_len = sizeof(dst4.sin_addr); } if (dst) { syslog(LOG_DEBUG, "calling get_src_for_route_to(%p, NULL, NULL, %p)", dst, &index); r = get_src_for_route_to (dst, NULL, NULL, &index); syslog(LOG_DEBUG, "get_src_for_route_to() returned %d", r); if(r >= 0) { syslog(LOG_DEBUG, "index=%d", index); } syslog(LOG_DEBUG, "calling get_src_for_route_to(%p, %p, %p(%u), %p)", dst, src, &src_len, (unsigned)src_len, &index); r = get_src_for_route_to (dst, src, &src_len, &index); syslog(LOG_DEBUG, "get_src_for_route_to() returned %d", r); if(r >= 0) { char src_str[128]; sockaddr_to_string(dst, src_str, sizeof(src_str)); syslog(LOG_DEBUG, "src=%s", src_str); syslog(LOG_DEBUG, "index=%d", index); } } closelog(); return 0; }
void client_new(struct player_control *player_control, int fd, const struct sockaddr *sa, size_t sa_length, int uid) { static unsigned int next_client_num; struct client *client; char *remote; assert(player_control != NULL); assert(fd >= 0); #ifdef HAVE_LIBWRAP if (sa->sa_family != AF_UNIX) { char *hostaddr = sockaddr_to_string(sa, sa_length, NULL); const char *progname = g_get_prgname(); struct request_info req; request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0); fromhost(&req); if (!hosts_access(&req)) { /* tcp wrappers says no */ g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE, "libwrap refused connection (libwrap=%s) from %s", progname, hostaddr); g_free(hostaddr); close_socket(fd); return; } g_free(hostaddr); } #endif /* HAVE_WRAP */ if (client_list_is_full()) { g_warning("Max Connections Reached!"); close_socket(fd); return; } client = g_new0(struct client, 1); client->player_control = player_control; client->channel = g_io_channel_new_socket(fd); /* GLib is responsible for closing the file descriptor */ g_io_channel_set_close_on_unref(client->channel, true); /* NULL encoding means the stream is binary safe; the MPD protocol is UTF-8 only, but we are doing this call anyway to prevent GLib from messing around with the stream */ g_io_channel_set_encoding(client->channel, NULL, NULL); /* we prefer to do buffering */ g_io_channel_set_buffered(client->channel, false); client->source_id = g_io_add_watch(client->channel, G_IO_IN|G_IO_ERR|G_IO_HUP, client_in_event, client); client->input = fifo_buffer_new(4096); client->permission = getDefaultPermissions(); client->uid = uid; client->last_activity = g_timer_new(); client->cmd_list = NULL; client->cmd_list_OK = -1; client->cmd_list_size = 0; client->deferred_send = g_queue_new(); client->deferred_bytes = 0; client->num = next_client_num++; client->send_buf_used = 0; client->subscriptions = NULL; client->messages = NULL; client->num_messages = 0; (void)send(fd, GREETING, sizeof(GREETING) - 1, 0); client_list_add(client); remote = sockaddr_to_string(sa, sa_length, NULL); g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE, "[%u] opened from %s", client->num, remote); g_free(remote); }
/* Responds to a SSDP "M-SEARCH" * s : socket to use * addr : peer * st, st_len : ST: header * suffix : suffix for USN: header * host, port : our HTTP host, port * delay : in milli-seconds */ static void SendSSDPResponse(int s, const struct sockaddr * addr, const char * st, int st_len, const char * suffix, const char * host, unsigned short http_port, #ifdef ENABLE_HTTPS unsigned short https_port, #endif const char * uuidvalue, unsigned int delay) { int l, n; char buf[SSDP_PACKET_MAX_LEN]; char addr_str[64]; socklen_t addrlen; int st_is_uuid; #ifdef ENABLE_HTTP_DATE char http_date[64]; time_t t; struct tm tm; time(&t); gmtime_r(&t, &tm); strftime(http_date, sizeof(http_date), "%a, %d %b %Y %H:%M:%S GMT", &tm); #endif st_is_uuid = (st_len == (int)strlen(uuidvalue)) && (memcmp(uuidvalue, st, st_len) == 0); /* * follow guideline from document "UPnP Device Architecture 1.0" * uppercase is recommended. * DATE: is recommended * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0 * - check what to put in the 'Cache-Control' header * * have a look at the document "UPnP Device Architecture v1.1 */ l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n" "CACHE-CONTROL: max-age=120\r\n" #ifdef ENABLE_HTTP_DATE "DATE: %s\r\n" #endif "ST: %.*s%s\r\n" "USN: %s%s%.*s%s\r\n" "EXT:\r\n" "SERVER: " MINIUPNPD_SERVER_STRING "\r\n" "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n" #ifdef ENABLE_HTTPS "SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n" #endif "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */ "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */ "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ "\r\n", #ifdef ENABLE_HTTP_DATE http_date, #endif st_len, st, suffix, uuidvalue, st_is_uuid ? "" : "::", st_is_uuid ? 0 : st_len, st, suffix, host, (unsigned int)http_port, #ifdef ENABLE_HTTPS host, (unsigned int)https_port, #endif upnp_bootid, upnp_bootid, upnp_configid); if(l<0) { syslog(LOG_ERR, "%s: snprintf failed %m", "SendSSDPResponse()"); return; } else if((unsigned)l>=sizeof(buf)) { syslog(LOG_WARNING, "%s: truncated output (%u>=%u)", "SendSSDPResponse()", (unsigned)l, (unsigned)sizeof(buf)); l = sizeof(buf) - 1; } addrlen = (addr->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); n = sendto_schedule(s, buf, l, 0, addr, addrlen, delay); sockaddr_to_string(addr, addr_str, sizeof(addr_str)); syslog(LOG_DEBUG, "%s: %d bytes to %s ST: %.*s", "SendSSDPResponse()", n, addr_str, l, buf); if(n < 0) { syslog(LOG_ERR, "%s: sendto(udp): %m", "SendSSDPResponse()"); } }
void ProcessSSDPData(int s, const char *bufr, int n, const struct sockaddr * sender, unsigned short port) { int i, l; struct lan_addr_s * lan_addr = NULL; const char * st = NULL; int st_len = 0; char sender_str[64]; const char * announced_host = NULL; /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 added */ char has_mx = 0; /* END 2082304944 zhoujianchun 00203875 2012.8.29 added */ /* get the string representation of the sender address */ sockaddr_to_string(sender, sender_str, sizeof(sender_str)); if(memcmp(bufr, "NOTIFY", 6) == 0) { /* ignore NOTIFY packets. We could log the sender and device type */ return; } else if(memcmp(bufr, "M-SEARCH", 8) == 0) { i = 0; while(i < n) { while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n')) i++; i += 2; /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 added */ if((i < n - 3) && (0 == strncasecmp(bufr+i, "mx:", 3))) { has_mx = 1; } /* END 2082304944 zhoujianchun 00203875 2012.8.29 added */ if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0)) { st = bufr+i+3; st_len = 0; while((*st == ' ' || *st == '\t') && (st < bufr + n)) st++; while(st[st_len]!='\r' && st[st_len]!='\n' && (st + st_len < bufr + n)) st_len++; } } /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 added */ if(0 == has_mx) { NP_UPNP_DEBUG("M-SEARCH, NO MX: RETURN.\n"); return; } /* END 2082304944 zhoujianchun 00203875 2012.8.29 added */ if(st && (st_len > 0)) { /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 added */ if(strncmp("upnp:rootdevice", st, st_len) && strncmp("ssdp:all", st, st_len) && strncmp("urn:schemas-upnp-org:device:InternetGatewayDevice:1", st, st_len) && strncmp("urn:schemas-upnp-org:device:WANConnectionDevice:1", st, st_len) && strncmp("urn:schemas-upnp-org:device:WANDevice:1", st, st_len) && strncmp("urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", st, st_len) && strncmp("urn:schemas-upnp-org:service:WANIPConnection:1", st, st_len) && strncmp("urn:schemas-upnp-org:service:Layer3Forwarding:1", st, st_len) && strncmp("urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", st, st_len) && strncmp(uuidvalue_root, st, st_len) && strncmp(uuidvalue_wan, st, st_len) && strncmp(uuidvalue_wan_conn, st, st_len) ) { NP_UPNP_DEBUG("ST IS NOT CORRECT, RETURN.\n"); return; } /* END 2082304944 zhoujianchun 00203875 2012.8.29 added */ /* TODO : doesnt answer at once but wait for a random time */ NP_UPNP_DEBUG("SSDP M-SEARCH from %s ST: %.*s\n", sender_str, st_len, st); /* find in which sub network the client is */ if(sender->sa_family == AF_INET) { for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if( (((const struct sockaddr_in *)sender)->sin_addr.s_addr & lan_addr->mask.s_addr) == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) break; } if (lan_addr == NULL) { NP_UPNP_ERROR("Can't find in which sub network the client is\n"); return; } announced_host = lan_addr->str; } #ifdef ENABLE_IPV6 else { /* IPv6 address with brackets */ announced_host = ipv6_addr_for_http_with_brackets; } #endif /* Responds to request with a device as ST header */ for(i = 0; known_service_types[i]; i++) { l = (int)strlen(known_service_types[i]); if(l<=st_len && (0 == memcmp(st, known_service_types[i], l))) { NP_UPNP_DEBUG("Single search found\n"); SendSSDPAnnounce2(s, sender, st, st_len, "", /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 modified */ announced_host, port, i); /* END 2082304944 zhoujianchun 00203875 2012.8.29 modified */ break; } } /* Responds to request with ST: ssdp:all */ /* strlen("ssdp:all") == 8 */ if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) { NP_UPNP_DEBUG("ssdp:all found\n"); for(i=0; known_service_types[i]; i++) { l = (int)strlen(known_service_types[i]); SendSSDPAnnounce2(s, sender, /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 modified */ known_service_types[i], l, i==0 ? "" : "1", announced_host, port, i); /* END 2082304944 zhoujianchun 00203875 2012.8.29 modified */ } /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 added */ for(i = 0; i < DEV_NUM; i++) { SendSSDPAnnounce2(s, sender, uuid_value[i], st_len, "", announced_host, port, -1); } /* END 2082304944 zhoujianchun 00203875 2012.8.29 added */ } /* responds to request by UUID value */ l = (int)strlen(uuidvalue); /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 modified */ if(l==st_len && 0 == memcmp(st, uuidvalue_root, l)) { NP_UPNP_DEBUG("ssdp:uuid found\n"); SendSSDPAnnounce2(s, sender, uuidvalue_root, st_len, "", announced_host, port, -1); } /* END 2082304944 zhoujianchun 00203875 2012.8.29 modified */ /* BEGIN 2082304944 zhoujianchun 00203875 2012.8.29 added */ else if(l==st_len && 0 == memcmp(st, uuidvalue_wan, l)) { NP_UPNP_DEBUG("ssdp:uuid found\n"); SendSSDPAnnounce2(s, sender, uuidvalue_wan, st_len, "", announced_host, port, -1); } else if(l==st_len && 0 == memcmp(st, uuidvalue_wan_conn, l)) { NP_UPNP_DEBUG("ssdp:uuid found\n"); SendSSDPAnnounce2(s, sender, uuidvalue_wan_conn, st_len, "", announced_host, port, -1); } /* END 2082304944 zhoujianchun 00203875 2012.8.29 added */ } else { NP_UPNP_DEBUG("Invalid SSDP M-SEARCH from %s\n", sender_str); } } else { NP_UPNP_DEBUG("Unknown udp packet received from %s\n", sender_str); } }
int get_src_for_route_to(const struct sockaddr * dst, void * src, size_t * src_len, int * index) { int fd = -1; struct nlmsghdr *h; int status; struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = (void*) &req.n, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; const struct sockaddr_in * dst4; const struct sockaddr_in6 * dst6; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_GETROUTE; req.r.rtm_family = dst->sa_family; req.r.rtm_table = 0; req.r.rtm_protocol = 0; req.r.rtm_scope = 0; req.r.rtm_type = 0; req.r.rtm_src_len = 0; req.r.rtm_dst_len = 0; req.r.rtm_tos = 0; { char dst_str[128]; sockaddr_to_string(dst, dst_str, sizeof(dst_str)); syslog(LOG_DEBUG, "get_src_for_route_to (%s)", dst_str); } /* add address */ if(dst->sa_family == AF_INET) { dst4 = (const struct sockaddr_in *)dst; nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst4->sin_addr, 4); req.r.rtm_dst_len = 32; } else { dst6 = (const struct sockaddr_in6 *)dst; nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst6->sin6_addr, 16); req.r.rtm_dst_len = 128; } fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { syslog(LOG_ERR, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m"); return -1; } memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; req.n.nlmsg_seq = 1; iov.iov_len = req.n.nlmsg_len; status = sendmsg(fd, &msg, 0); if (status < 0) { syslog(LOG_ERR, "sendmsg(rtnetlink) : %m"); goto error; } memset(&req, 0, sizeof(req)); for(;;) { iov.iov_len = sizeof(req); status = recvmsg(fd, &msg, 0); if(status < 0) { if (errno == EINTR || errno == EAGAIN) continue; syslog(LOG_ERR, "recvmsg(rtnetlink) %m"); goto error; } if(status == 0) { syslog(LOG_ERR, "recvmsg(rtnetlink) EOF"); goto error; } for (h = (struct nlmsghdr*)&req.n; status >= (int)sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); if (l<0 || len>status) { if (msg.msg_flags & MSG_TRUNC) { syslog(LOG_ERR, "Truncated message"); } syslog(LOG_ERR, "malformed message: len=%d", len); goto error; } if(nladdr.nl_pid != 0 || h->nlmsg_seq != 1/*seq*/) { syslog(LOG_ERR, "wrong seq = %d\n", h->nlmsg_seq); /* Don't forget to skip that message. */ status -= NLMSG_ALIGN(len); h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); continue; } if(h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); syslog(LOG_ERR, "NLMSG_ERROR %d : %s", err->error, strerror(-err->error)); goto error; } if(h->nlmsg_type == RTM_NEWROUTE) { struct rtattr * rta; int len = h->nlmsg_len; len -= NLMSG_LENGTH(sizeof(struct rtmsg)); for(rta = RTM_RTA(NLMSG_DATA((h))); RTA_OK(rta, len); rta = RTA_NEXT(rta,len)) { unsigned char * data = RTA_DATA(rta); if(rta->rta_type == RTA_PREFSRC) { if(src_len && src) { if(*src_len < RTA_PAYLOAD(rta)) { syslog(LOG_WARNING, "cannot copy src: %u<%lu", (unsigned)*src_len, RTA_PAYLOAD(rta)); goto error; } *src_len = RTA_PAYLOAD(rta); memcpy(src, data, RTA_PAYLOAD(rta)); } } else if(rta->rta_type == RTA_OIF) { if(index) memcpy(index, data, sizeof(int)); } } close(fd); return 0; } status -= NLMSG_ALIGN(len); h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); } } syslog(LOG_WARNING, "get_src_for_route_to() : src not found"); error: if(fd >= 0) close(fd); return -1; }
static mrb_value sysctl_net_list(mrb_state *mrb, mrb_value self) { const char *ifname = NULL; struct ifaddrs *addrs, *curr; mrb_value r_ret; struct RClass *if_class = mrb_class_get(mrb, "NetworkAddress"); mrb_get_args(mrb, "|z", &ifname); if( getifaddrs(&addrs) == -1 ){ mrb_raisef(mrb, E_RUNTIME_ERROR, "getifaddrs: %S", mrb_str_new_cstr(mrb, strerror(errno))); goto ret; } if( ifname ){ r_ret = mrb_ary_new(mrb); } else { r_ret = mrb_hash_new(mrb); } curr = addrs; while( curr != NULL ){ char addr[INET6_ADDRSTRLEN]; char netmask[INET6_ADDRSTRLEN]; if( curr->ifa_netmask != NULL ){ mrb_value r_key, r_addr, r_list; // only return wanted infos if( !ifname || !strcmp(ifname, curr->ifa_name) ){ if( sockaddr_to_string(curr->ifa_addr, addr, sizeof(addr)) == -1) continue; if( sockaddr_to_string(curr->ifa_netmask, netmask, sizeof(netmask)) == -1) continue; r_key = mrb_str_new_cstr(mrb, curr->ifa_name); r_addr = mrb_funcall(mrb, mrb_obj_value(if_class), "new", 2, mrb_str_new_cstr(mrb, addr), mrb_str_new_cstr(mrb, netmask) ); if( ifname ){ mrb_ary_push(mrb, r_ret, r_addr); } else { // check if key exists r_list = mrb_hash_get(mrb, r_ret, r_key); if( mrb_nil_p(r_list) ){ r_list = mrb_ary_new(mrb); } mrb_ary_push(mrb, r_list, r_addr); mrb_hash_set(mrb, r_ret, r_key, r_list); } } } curr = curr->ifa_next; } ret: return r_ret; }
void ProcessSSDPData(int s, const char *bufr, int n, const struct sockaddr * sender, unsigned short port) { int i, l; struct lan_addr_s * lan_addr = NULL; const char * st = NULL; int st_len = 0; char sender_str[64]; const char * announced_host = NULL; #ifdef UPNP_STRICT char announced_host_buf[64]; #endif /* get the string representation of the sender address */ sockaddr_to_string(sender, sender_str, sizeof(sender_str)); if(memcmp(bufr, "NOTIFY", 6) == 0) { /* ignore NOTIFY packets. We could log the sender and device type */ return; } else if(memcmp(bufr, "M-SEARCH", 8) == 0) { i = 0; while(i < n) { while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n')) i++; i += 2; if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0)) { st = bufr+i+3; st_len = 0; while((*st == ' ' || *st == '\t') && (st < bufr + n)) st++; while(st[st_len]!='\r' && st[st_len]!='\n' && (st + st_len < bufr + n)) st_len++; /*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/ /*j = 0;*/ /*while(bufr[i+j]!='\r') j++;*/ /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/ } } /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s", sender_str );*/ if(st && (st_len > 0)) { /* TODO : doesnt answer at once but wait for a random time */ syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s", sender_str, st_len, st); /* find in which sub network the client is */ if(sender->sa_family == AF_INET) { for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if( (((const struct sockaddr_in *)sender)->sin_addr.s_addr & lan_addr->mask.s_addr) == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) break; } if (lan_addr == NULL) { syslog(LOG_ERR, "Can't find in which sub network the client is"); return; } announced_host = lan_addr->str; } #ifdef ENABLE_IPV6 else { /* IPv6 address with brackets */ #ifdef UPNP_STRICT struct in6_addr addr6; size_t addr6_len = sizeof(addr6); /* retrieve the IPv6 address which * will be used locally to reach sender */ memset(&addr6, 0, sizeof(addr6)); if(get_src_for_route_to (sender, &addr6, &addr6_len) < 0) { syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets); announced_host = ipv6_addr_for_http_with_brackets; } else { if(inet_ntop(AF_INET6, &addr6, announced_host_buf+1, sizeof(announced_host_buf) - 2)) { announced_host_buf[0] = '['; i = strlen(announced_host_buf); if(i < (int)sizeof(announced_host_buf) - 1) { announced_host_buf[i] = ']'; announced_host_buf[i+1] = '\0'; } else { syslog(LOG_NOTICE, "cannot suffix %s with ']'", announced_host_buf); } announced_host = announced_host_buf; } else { syslog(LOG_NOTICE, "inet_ntop() failed %m"); announced_host = ipv6_addr_for_http_with_brackets; } } #else announced_host = ipv6_addr_for_http_with_brackets; #endif } #endif /* Responds to request with a device as ST header */ for(i = 0; known_service_types[i]; i++) { l = (int)strlen(known_service_types[i]); if(l<=st_len && (0 == memcmp(st, known_service_types[i], l))) { syslog(LOG_INFO, "Single search found"); SendSSDPAnnounce2(s, sender, st, st_len, "", announced_host, port); break; } } /* Responds to request with ST: ssdp:all */ /* strlen("ssdp:all") == 8 */ if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) { syslog(LOG_INFO, "ssdp:all found"); for(i=0; known_service_types[i]; i++) { l = (int)strlen(known_service_types[i]); SendSSDPAnnounce2(s, sender, known_service_types[i], l, i==0?"":"1", announced_host, port); } } /* responds to request by UUID value */ l = (int)strlen(uuidvalue); if(l==st_len && (0 == memcmp(st, uuidvalue, l))) { syslog(LOG_INFO, "ssdp:uuid found"); SendSSDPAnnounce2(s, sender, st, st_len, "", announced_host, port); } } else { syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str); } } else { syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str); } }
/* process HTTP or SSDP requests */ int main(int argc, char * * argv) { int i; int shttpl = -1; /* socket for HTTP */ int sudp = -1; /* IP v4 socket for receiving SSDP */ #ifdef ENABLE_IPV6 int sudpv6 = -1; /* IP v6 socket for receiving SSDP */ #endif #ifdef ENABLE_NATPMP int * snatpmp = NULL; #endif #ifdef ENABLE_NFQUEUE int nfqh = -1; #endif #ifdef USE_IFACEWATCHER int sifacewatcher = -1; #endif int * snotify = NULL; int addr_count; LIST_HEAD(httplisthead, upnphttp) upnphttphead; struct upnphttp * e = 0; struct upnphttp * next; fd_set readset; /* for select() */ fd_set writeset; struct timeval timeout, timeofday, lasttimeofday = {0, 0}; int max_fd = -1; #ifdef USE_MINIUPNPDCTL int sctl = -1; LIST_HEAD(ctlstructhead, ctlelem) ctllisthead; struct ctlelem * ectl; struct ctlelem * ectlnext; #endif struct runtime_vars v; /* variables used for the unused-rule cleanup process */ struct rule_state * rule_list = 0; struct timeval checktime = {0, 0}; struct lan_addr_s * lan_addr; #ifdef ENABLE_6FC_SERVICE unsigned int next_pinhole_ts; #endif if(init(argc, argv, &v) != 0) return 1; /* count lan addrs */ addr_count = 0; for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) addr_count++; if(addr_count > 0) { #ifndef ENABLE_IPV6 snotify = calloc(addr_count, sizeof(int)); #else /* one for IPv4, one for IPv6 */ snotify = calloc(addr_count * 2, sizeof(int)); #endif } #ifdef ENABLE_NATPMP if(addr_count > 0) { snatpmp = malloc(addr_count * sizeof(int)); for(i = 0; i < addr_count; i++) snatpmp[i] = -1; } #endif LIST_INIT(&upnphttphead); #ifdef USE_MINIUPNPDCTL LIST_INIT(&ctllisthead); #endif if( #ifdef ENABLE_NATPMP !GETFLAG(ENABLENATPMPMASK) && #endif !GETFLAG(ENABLEUPNPMASK) ) { syslog(LOG_ERR, "Why did you run me anyway?"); return 0; } syslog(LOG_INFO, "Starting%s%swith external interface %s", #ifdef ENABLE_NATPMP GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP " : " ", #else " ", #endif GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "", ext_if_name); if(GETFLAG(ENABLEUPNPMASK)) { /* open socket for HTTP connections. Listen on the 1st LAN address */ shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0); if(shttpl < 0) { syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING"); return 1; } if(v.port <= 0) { struct sockaddr_in sockinfo; socklen_t len = sizeof(struct sockaddr_in); if (getsockname(shttpl, (struct sockaddr *)&sockinfo, &len) < 0) { syslog(LOG_ERR, "getsockname(): %m"); return 1; } v.port = ntohs(sockinfo.sin_port); } syslog(LOG_NOTICE, "HTTP listening on port %d", v.port); #ifdef ENABLE_IPV6 if(find_ipv6_addr(NULL, ipv6_addr_for_http_with_brackets, sizeof(ipv6_addr_for_http_with_brackets)) > 0) { syslog(LOG_NOTICE, "HTTP IPv6 address given to control points : %s", ipv6_addr_for_http_with_brackets); } else { memcpy(ipv6_addr_for_http_with_brackets, "[::1]", 6); syslog(LOG_WARNING, "no HTTP IPv6 address"); } #endif /* open socket for SSDP connections */ sudp = OpenAndConfSSDPReceiveSocket(0); if(sudp < 0) { syslog(LOG_NOTICE, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd"); if(SubmitServicesToMiniSSDPD(lan_addrs.lh_first->str, v.port) < 0) { syslog(LOG_ERR, "Failed to connect to MiniSSDPd. EXITING"); return 1; } } #ifdef ENABLE_IPV6 sudpv6 = OpenAndConfSSDPReceiveSocket(1); if(sudpv6 < 0) { syslog(LOG_WARNING, "Failed to open socket for receiving SSDP (IP v6)."); } #endif /* open socket for sending notifications */ if(OpenAndConfSSDPNotifySockets(snotify) < 0) { syslog(LOG_ERR, "Failed to open sockets for sending SSDP notify " "messages. EXITING"); return 1; } #ifdef USE_IFACEWATCHER /* open socket for kernel notifications about new network interfaces */ if (sudp >= 0) { sifacewatcher = OpenAndConfInterfaceWatchSocket(); if (sifacewatcher < 0) { syslog(LOG_ERR, "Failed to open socket for receiving network interface notifications"); } } #endif } #ifdef ENABLE_NATPMP /* open socket for NAT PMP traffic */ if(GETFLAG(ENABLENATPMPMASK)) { if(OpenAndConfNATPMPSockets(snatpmp) < 0) { syslog(LOG_ERR, "Failed to open sockets for NAT PMP."); } else { syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u", NATPMP_PORT); } #if 0 ScanNATPMPforExpiration(); #endif } #endif /* for miniupnpdctl */ #ifdef USE_MINIUPNPDCTL sctl = OpenAndConfCtlUnixSocket("/var/run/miniupnpd.ctl"); #endif #ifdef ENABLE_NFQUEUE if ( nfqueue != -1 && n_nfqix > 0) { nfqh = OpenAndConfNFqueue(); if(nfqh < 0) { syslog(LOG_ERR, "Failed to open fd for NFQUEUE."); return 1; } else { syslog(LOG_NOTICE, "Opened NFQUEUE %d",nfqueue); } } #endif /* main loop */ while(!quitting) { /* Correct startup_time if it was set with a RTC close to 0 */ if((startup_time<60*60*24) && (time(NULL)>60*60*24)) { set_startup_time(GETFLAG(SYSUPTIMEMASK)); } /* send public address change notifications if needed */ if(should_send_public_address_change_notif) { syslog(LOG_DEBUG, "should send external iface address change notification(s)"); #ifdef ENABLE_NATPMP if(GETFLAG(ENABLENATPMPMASK)) SendNATPMPPublicAddressChangeNotification(snatpmp, addr_count); #endif #ifdef ENABLE_EVENTS if(GETFLAG(ENABLEUPNPMASK)) { upnp_event_var_change_notify(EWanIPC); } #endif should_send_public_address_change_notif = 0; } /* Check if we need to send SSDP NOTIFY messages and do it if * needed */ if(gettimeofday(&timeofday, 0) < 0) { syslog(LOG_ERR, "gettimeofday(): %m"); timeout.tv_sec = v.notify_interval; timeout.tv_usec = 0; } else { /* the comparaison is not very precise but who cares ? */ if(timeofday.tv_sec >= (lasttimeofday.tv_sec + v.notify_interval)) { if (GETFLAG(ENABLEUPNPMASK)) SendSSDPNotifies2(snotify, (unsigned short)v.port, v.notify_interval << 1); memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval)); timeout.tv_sec = v.notify_interval; timeout.tv_usec = 0; } else { timeout.tv_sec = lasttimeofday.tv_sec + v.notify_interval - timeofday.tv_sec; if(timeofday.tv_usec > lasttimeofday.tv_usec) { timeout.tv_usec = 1000000 + lasttimeofday.tv_usec - timeofday.tv_usec; timeout.tv_sec--; } else { timeout.tv_usec = lasttimeofday.tv_usec - timeofday.tv_usec; } } } /* remove unused rules */ if( v.clean_ruleset_interval && (timeofday.tv_sec >= checktime.tv_sec + v.clean_ruleset_interval)) { if(rule_list) { remove_unused_rules(rule_list); rule_list = NULL; } else { rule_list = get_upnp_rules_state_list(v.clean_ruleset_threshold); } memcpy(&checktime, &timeofday, sizeof(struct timeval)); } /* Remove expired port mappings, based on UPnP IGD LeaseDuration * or NAT-PMP lifetime) */ if(nextruletoclean_timestamp && ((unsigned int)timeofday.tv_sec >= nextruletoclean_timestamp)) { syslog(LOG_DEBUG, "cleaning expired Port Mappings"); get_upnp_rules_state_list(0); } if(nextruletoclean_timestamp && ((unsigned int)timeout.tv_sec >= (nextruletoclean_timestamp - timeofday.tv_sec))) { timeout.tv_sec = nextruletoclean_timestamp - timeofday.tv_sec; timeout.tv_usec = 0; syslog(LOG_DEBUG, "setting timeout to %u sec", (unsigned)timeout.tv_sec); } #ifdef ENABLE_NATPMP #if 0 /* Remove expired NAT-PMP mappings */ while(nextnatpmptoclean_timestamp && (timeofday.tv_sec >= nextnatpmptoclean_timestamp + startup_time)) { /*syslog(LOG_DEBUG, "cleaning expired NAT-PMP mappings");*/ if(CleanExpiredNATPMP() < 0) { syslog(LOG_ERR, "CleanExpiredNATPMP() failed"); break; } } if(nextnatpmptoclean_timestamp && timeout.tv_sec >= (nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec)) { /*syslog(LOG_DEBUG, "setting timeout to %d sec", nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec);*/ timeout.tv_sec = nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec; timeout.tv_usec = 0; } #endif #endif #ifdef ENABLE_6FC_SERVICE /* Clean up expired IPv6 PinHoles */ next_pinhole_ts = 0; upnp_clean_expired_pinholes(&next_pinhole_ts); if(next_pinhole_ts && timeout.tv_sec >= (int)(next_pinhole_ts - timeofday.tv_sec)) { timeout.tv_sec = next_pinhole_ts - timeofday.tv_sec; timeout.tv_usec = 0; } #endif /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ FD_ZERO(&readset); FD_ZERO(&writeset); if (sudp >= 0) { FD_SET(sudp, &readset); max_fd = MAX( max_fd, sudp); #ifdef USE_IFACEWATCHER if (sifacewatcher >= 0) { FD_SET(sifacewatcher, &readset); max_fd = MAX(max_fd, sifacewatcher); } #endif } if (shttpl >= 0) { FD_SET(shttpl, &readset); max_fd = MAX( max_fd, shttpl); } #ifdef ENABLE_IPV6 if (sudpv6 >= 0) { FD_SET(sudpv6, &readset); max_fd = MAX( max_fd, sudpv6); } #endif #ifdef ENABLE_NFQUEUE if (nfqh >= 0) { FD_SET(nfqh, &readset); max_fd = MAX( max_fd, nfqh); } #endif i = 0; /* active HTTP connections count */ for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if(e->socket >= 0) { if(e->state <= EWaitingForHttpContent) FD_SET(e->socket, &readset); else if(e->state == ESendingAndClosing) FD_SET(e->socket, &writeset); else continue; max_fd = MAX(max_fd, e->socket); i++; } } /* for debug */ #ifdef DEBUG if(i > 1) { syslog(LOG_DEBUG, "%d active incoming HTTP connections", i); } #endif #ifdef ENABLE_NATPMP for(i=0; i<addr_count; i++) { if(snatpmp[i] >= 0) { FD_SET(snatpmp[i], &readset); max_fd = MAX( max_fd, snatpmp[i]); } } #endif #ifdef USE_MINIUPNPDCTL if(sctl >= 0) { FD_SET(sctl, &readset); max_fd = MAX( max_fd, sctl); } for(ectl = ctllisthead.lh_first; ectl; ectl = ectl->entries.le_next) { if(ectl->socket >= 0) { FD_SET(ectl->socket, &readset); max_fd = MAX( max_fd, ectl->socket); } } #endif #ifdef ENABLE_EVENTS upnpevents_selectfds(&readset, &writeset, &max_fd); #endif if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) { if(quitting) goto shutdown; if(errno == EINTR) continue; /* interrupted by a signal, start again */ syslog(LOG_ERR, "select(all): %m"); syslog(LOG_ERR, "Failed to select open sockets. EXITING"); return 1; /* very serious cause of error */ } #ifdef USE_MINIUPNPDCTL for(ectl = ctllisthead.lh_first; ectl;) { ectlnext = ectl->entries.le_next; if((ectl->socket >= 0) && FD_ISSET(ectl->socket, &readset)) { char buf[256]; int l; l = read(ectl->socket, buf, sizeof(buf)); if(l > 0) { /*write(ectl->socket, buf, l);*/ write_command_line(ectl->socket, argc, argv); write_option_list(ectl->socket); write_permlist(ectl->socket, upnppermlist, num_upnpperm); write_upnphttp_details(ectl->socket, upnphttphead.lh_first); write_ctlsockets_list(ectl->socket, ctllisthead.lh_first); write_ruleset_details(ectl->socket); #ifdef ENABLE_EVENTS write_events_details(ectl->socket); #endif /* close the socket */ close(ectl->socket); ectl->socket = -1; } else { close(ectl->socket); ectl->socket = -1; } } if(ectl->socket < 0) { LIST_REMOVE(ectl, entries); free(ectl); } ectl = ectlnext; } if((sctl >= 0) && FD_ISSET(sctl, &readset)) { int s; struct sockaddr_un clientname; struct ctlelem * tmp; socklen_t clientnamelen = sizeof(struct sockaddr_un); //syslog(LOG_DEBUG, "sctl!"); s = accept(sctl, (struct sockaddr *)&clientname, &clientnamelen); syslog(LOG_DEBUG, "sctl! : '%s'", clientname.sun_path); tmp = malloc(sizeof(struct ctlelem)); tmp->socket = s; LIST_INSERT_HEAD(&ctllisthead, tmp, entries); } #endif #ifdef ENABLE_EVENTS upnpevents_processfds(&readset, &writeset); #endif #ifdef ENABLE_NATPMP /* process NAT-PMP packets */ for(i=0; i<addr_count; i++) { if((snatpmp[i] >= 0) && FD_ISSET(snatpmp[i], &readset)) { ProcessIncomingNATPMPPacket(snatpmp[i]); } } #endif /* process SSDP packets */ if(sudp >= 0 && FD_ISSET(sudp, &readset)) { /*syslog(LOG_INFO, "Received UDP Packet");*/ ProcessSSDPRequest(sudp, (unsigned short)v.port); } #ifdef ENABLE_IPV6 if(sudpv6 >= 0 && FD_ISSET(sudpv6, &readset)) { syslog(LOG_INFO, "Received UDP Packet (IPv6)"); ProcessSSDPRequest(sudpv6, (unsigned short)v.port); } #endif #ifdef USE_IFACEWATCHER /* process kernel notifications */ if (sifacewatcher >= 0 && FD_ISSET(sifacewatcher, &readset)) ProcessInterfaceWatchNotify(sifacewatcher); #endif /* process active HTTP connections */ /* LIST_FOREACH macro is not available under linux */ for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if(e->socket >= 0) { if(FD_ISSET(e->socket, &readset) || FD_ISSET(e->socket, &writeset)) { Process_upnphttp(e); } } } /* process incoming HTTP connections */ if(shttpl >= 0 && FD_ISSET(shttpl, &readset)) { int shttp; socklen_t clientnamelen; #ifdef ENABLE_IPV6 struct sockaddr_storage clientname; clientnamelen = sizeof(struct sockaddr_storage); #else struct sockaddr_in clientname; clientnamelen = sizeof(struct sockaddr_in); #endif shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen); if(shttp<0) { /* ignore EAGAIN, EWOULDBLOCK, EINTR, we just try again later */ if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) syslog(LOG_ERR, "accept(http): %m"); } else { struct upnphttp * tmp = 0; char addr_str[64]; sockaddr_to_string((struct sockaddr *)&clientname, addr_str, sizeof(addr_str)); syslog(LOG_INFO, "HTTP connection from %s", addr_str); /* Create a new upnphttp object and add it to * the active upnphttp object list */ tmp = New_upnphttp(shttp); if(tmp) { #ifdef ENABLE_IPV6 if(clientname.ss_family == AF_INET) { tmp->clientaddr = ((struct sockaddr_in *)&clientname)->sin_addr; } else if(clientname.ss_family == AF_INET6) { struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&clientname; if(IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) { memcpy(&tmp->clientaddr, &addr->sin6_addr.s6_addr[12], 4); } else { tmp->ipv6 = 1; memcpy(&tmp->clientaddr_v6, &addr->sin6_addr, sizeof(struct in6_addr)); } } #else tmp->clientaddr = clientname.sin_addr; #endif LIST_INSERT_HEAD(&upnphttphead, tmp, entries); } else { syslog(LOG_ERR, "New_upnphttp() failed"); close(shttp); } } } #ifdef ENABLE_NFQUEUE /* process NFQ packets */ if(nfqh >= 0 && FD_ISSET(nfqh, &readset)) { /* syslog(LOG_INFO, "Received NFQUEUE Packet");*/ ProcessNFQUEUE(nfqh); } #endif /* delete finished HTTP connections */ for(e = upnphttphead.lh_first; e != NULL; ) { next = e->entries.le_next; if(e->state >= EToDelete) { LIST_REMOVE(e, entries); Delete_upnphttp(e); } e = next; } } /* end of main loop */ shutdown: /* close out open sockets */ while(upnphttphead.lh_first != NULL) { e = upnphttphead.lh_first; LIST_REMOVE(e, entries); Delete_upnphttp(e); } if (sudp >= 0) close(sudp); if (shttpl >= 0) close(shttpl); #ifdef ENABLE_IPV6 if (sudpv6 >= 0) close(sudpv6); #endif #ifdef USE_IFACEWATCHER if(sifacewatcher >= 0) close(sifacewatcher); #endif #ifdef ENABLE_NATPMP for(i=0; i<addr_count; i++) { if(snatpmp[i]>=0) { close(snatpmp[i]); snatpmp[i] = -1; } } #endif #ifdef USE_MINIUPNPDCTL if(sctl>=0) { close(sctl); sctl = -1; if(unlink("/var/run/miniupnpd.ctl") < 0) { syslog(LOG_ERR, "unlink() %m"); } } #endif if (GETFLAG(ENABLEUPNPMASK)) { #ifndef ENABLE_IPV6 if(SendSSDPGoodbye(snotify, addr_count) < 0) #else if(SendSSDPGoodbye(snotify, addr_count * 2) < 0) #endif { syslog(LOG_ERR, "Failed to broadcast good-bye notifications"); } #ifndef ENABLE_IPV6 for(i = 0; i < addr_count; i++) #else for(i = 0; i < addr_count * 2; i++) #endif close(snotify[i]); } if(pidfilename && (unlink(pidfilename) < 0)) { syslog(LOG_ERR, "Failed to remove pidfile %s: %m", pidfilename); } /* delete lists */ while(lan_addrs.lh_first != NULL) { lan_addr = lan_addrs.lh_first; LIST_REMOVE(lan_addrs.lh_first, list); free(lan_addr); } #ifdef ENABLE_NATPMP free(snatpmp); #endif free(snotify); closelog(); #ifndef DISABLE_CONFIG_FILE freeoptions(); #endif return 0; }