diff options
Diffstat (limited to 'libsysutils/src')
-rw-r--r-- | libsysutils/src/FrameworkCommand.cpp | 5 | ||||
-rw-r--r-- | libsysutils/src/FrameworkListener.cpp | 4 | ||||
-rw-r--r-- | libsysutils/src/NetlinkEvent.cpp | 126 | ||||
-rw-r--r-- | libsysutils/src/SocketClient.cpp | 59 | ||||
-rw-r--r-- | libsysutils/src/SocketListener.cpp | 127 |
5 files changed, 266 insertions, 55 deletions
diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp index 038d87e..0b95a81 100644 --- a/libsysutils/src/FrameworkCommand.cpp +++ b/libsysutils/src/FrameworkCommand.cpp @@ -21,11 +21,14 @@ #include <sysutils/FrameworkCommand.h> +#define UNUSED __attribute__((unused)) + FrameworkCommand::FrameworkCommand(const char *cmd) { mCommand = cmd; } -int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) { +int FrameworkCommand::runCommand(SocketClient *c UNUSED, int argc UNUSED, + char **argv UNUSED) { SLOGW("Command %s has no run handler!", getCommand()); errno = ENOSYS; return -1; diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 02a401d..a5ffda2 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -27,6 +27,8 @@ static const int CMD_BUF_SIZE = 1024; +#define UNUSED __attribute__((unused)) + FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : SocketListener(socketName, true, withSeq) { init(socketName, withSeq); @@ -37,7 +39,7 @@ FrameworkListener::FrameworkListener(const char *socketName) : init(socketName, false); } -void FrameworkListener::init(const char *socketName, bool withSeq) { +void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; mCommandCount = 0; diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index aae2ae7..34f2016 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -24,6 +24,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> +#include <netinet/icmp6.h> #include <arpa/inet.h> #include <net/if.h> @@ -44,6 +45,7 @@ const int NetlinkEvent::NlActionLinkUp = 4; const int NetlinkEvent::NlActionLinkDown = 5; const int NetlinkEvent::NlActionAddressUpdated = 6; const int NetlinkEvent::NlActionAddressRemoved = 7; +const int NetlinkEvent::NlActionRdnss = 8; NetlinkEvent::NetlinkEvent() { mAction = NlActionUnknown; @@ -76,7 +78,7 @@ void NetlinkEvent::dump() { } /* - * Decode a RTM_NEWADDR or RTM_DELADDR message. + * Parse a RTM_NEWADDR or RTM_DELADDR message. */ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize) { @@ -172,13 +174,112 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } /* - * Parse an binary message from a NETLINK_ROUTE netlink socket. +<<<<<<< HEAD + * Parse a RTM_NEWNDUSEROPT message. + */ +bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) { + // Check the length is valid. + if (msg->nduseropt_opts_len > len) { + SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n", + msg->nduseropt_opts_len, len); + return false; + } + len = msg->nduseropt_opts_len; + + // Check address family and packet type. + if (msg->nduseropt_family != AF_INET6) { + SLOGE("RTM_NEWNDUSEROPT message for unknown family %d\n", + msg->nduseropt_family); + return false; + } + + if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT || + msg->nduseropt_icmp_code != 0) { + SLOGE("RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\n", + msg->nduseropt_icmp_type, msg->nduseropt_icmp_code); + return false; + } + + // Find the interface name. + char ifname[IFNAMSIZ + 1]; + if (!if_indextoname(msg->nduseropt_ifindex, ifname)) { + SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n", + msg->nduseropt_ifindex); + return false; + } + + // The kernel sends a separate netlink message for each ND option in the RA. + // So only parse the first ND option in the message. + struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1); + + // The length is in multiples of 8 octets. + uint16_t optlen = opthdr->nd_opt_len; + if (optlen * 8 > len) { + SLOGE("Invalid option length %d > %d for ND option %d\n", + optlen * 8, len, opthdr->nd_opt_type); + return false; + } + + if (opthdr->nd_opt_type == ND_OPT_RDNSS) { + // DNS Servers (RFC 6106). + // Each address takes up 2*8 octets, and the header takes up 8 octets. + // So for a valid option with one or more addresses, optlen must be + // odd and greater than 1. + if ((optlen < 3) || !(optlen & 0x1)) { + SLOGE("Invalid optlen %d for RDNSS option\n", optlen); + return false; + } + int numaddrs = (optlen - 1) / 2; + + // Find the lifetime. + struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr; + uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime); + + // Construct "SERVERS=<comma-separated string of DNS addresses>". + // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the + // the last address are followed by ','; the last is followed by '\0'. + static const char kServerTag[] = "SERVERS="; + static const int kTagLength = sizeof(kServerTag) - 1; + int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1); + char *buf = (char *) malloc(bufsize); + if (!buf) { + SLOGE("RDNSS option: out of memory\n"); + return false; + } + strcpy(buf, kServerTag); + int pos = kTagLength; + + struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1); + for (int i = 0; i < numaddrs; i++) { + if (i > 0) { + buf[pos++] = ','; + } + inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos); + pos += strlen(buf + pos); + } + buf[pos] = '\0'; + + mAction = NlActionRdnss; + mSubsystem = strdup("net"); + asprintf(&mParams[0], "INTERFACE=%s", ifname); + asprintf(&mParams[1], "LIFETIME=%u", lifetime); + mParams[2] = buf; + } else { + SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type); + return false; + } + + return true; +} + +/* + * Parse a binary message from a NETLINK_ROUTE netlink socket. */ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { const struct nlmsghdr *nh; for (nh = (struct nlmsghdr *) buffer; - NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE); + NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE); nh = NLMSG_NEXT(nh, size)) { if (nh->nlmsg_type == RTM_NEWLINK) { @@ -245,8 +346,25 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) { continue; } + + } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) { + int len = nh->nlmsg_len - sizeof(*nh); + struct nduseroptmsg *ndmsg = (struct nduseroptmsg *) NLMSG_DATA(nh); + + if (sizeof(*ndmsg) > (size_t) len) { + SLOGE("Got a short RTM_NEWNDUSEROPT message\n"); + continue; + } + + size_t optsize = NLMSG_PAYLOAD(nh, sizeof(*ndmsg)); + if (!parseNdUserOptMessage(ndmsg, optsize)) { + continue; + } + + } else { - SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type); + SLOGD("Unexpected netlink message. type=0x%x\n", + nh->nlmsg_type); } } diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp index ae0e077..3625d93 100644 --- a/libsysutils/src/SocketClient.cpp +++ b/libsysutils/src/SocketClient.cpp @@ -71,7 +71,7 @@ int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdN ret = asprintf(&buf, "%d %s", code, msg); } } - /* Send the zero-terminated message */ + // Send the zero-terminated message if (ret != -1) { ret = sendMsg(buf); free(buf); @@ -79,22 +79,25 @@ int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdN return ret; } -/** send 3-digit code, null, binary-length, binary data */ +// send 3-digit code, null, binary-length, binary data int SocketClient::sendBinaryMsg(int code, const void *data, int len) { - /* 4 bytes for the code & null + 4 bytes for the len */ + // 4 bytes for the code & null + 4 bytes for the len char buf[8]; - /* Write the code */ + // Write the code snprintf(buf, 4, "%.3d", code); - /* Write the len */ + // Write the len uint32_t tmp = htonl(len); memcpy(buf + 4, &tmp, sizeof(uint32_t)); + struct iovec vec[2]; + vec[0].iov_base = (void *) buf; + vec[0].iov_len = sizeof(buf); + vec[1].iov_base = (void *) data; + vec[1].iov_len = len; + pthread_mutex_lock(&mWriteMutex); - int result = sendDataLocked(buf, sizeof(buf)); - if (result == 0 && len > 0) { - result = sendDataLocked(data, len); - } + int result = sendDataLockedv(vec, (len > 0) ? 2 : 1); pthread_mutex_unlock(&mWriteMutex); return result; @@ -147,33 +150,51 @@ int SocketClient::sendMsg(const char *msg) { } int SocketClient::sendData(const void *data, int len) { + struct iovec vec[1]; + vec[0].iov_base = (void *) data; + vec[0].iov_len = len; pthread_mutex_lock(&mWriteMutex); - int rc = sendDataLocked(data, len); + int rc = sendDataLockedv(vec, 1); pthread_mutex_unlock(&mWriteMutex); return rc; } -int SocketClient::sendDataLocked(const void *data, int len) { - int rc = 0; - const char *p = (const char*) data; - int brtw = len; +int SocketClient::sendDatav(struct iovec *iov, int iovcnt) { + pthread_mutex_lock(&mWriteMutex); + int rc = sendDataLockedv(iov, iovcnt); + pthread_mutex_unlock(&mWriteMutex); + + return rc; +} + +int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) { if (mSocket < 0) { errno = EHOSTUNREACH; return -1; } - if (len == 0) { + if (iovcnt <= 0) { return 0; } - while (brtw > 0) { - rc = send(mSocket, p, brtw, MSG_NOSIGNAL); + int current = 0; + + for (;;) { + ssize_t rc = writev(mSocket, iov + current, iovcnt - current); if (rc > 0) { - p += rc; - brtw -= rc; + size_t written = rc; + while ((current < iovcnt) && (written >= iov[current].iov_len)) { + written -= iov[current].iov_len; + current++; + } + if (current == iovcnt) { + break; + } + iov[current].iov_base = (char *)iov[current].iov_base + written; + iov[current].iov_len -= written; continue; } diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 0361641..5c75206 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,8 @@ #include <sysutils/SocketListener.h> #include <sysutils/SocketClient.h> -#define LOG_NDEBUG 0 +#define CtrlPipe_Shutdown 0 +#define CtrlPipe_Wakeup 1 SocketListener::SocketListener(const char *socketName, bool listen) { init(socketName, -1, listen, false); @@ -103,7 +104,7 @@ int SocketListener::startListener() { } int SocketListener::stopListener() { - char c = 0; + char c = CtrlPipe_Shutdown; int rc; rc = TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &c, 1)); @@ -145,7 +146,7 @@ void *SocketListener::threadStart(void *obj) { void SocketListener::runListener() { - SocketClientCollection *pendingList = new SocketClientCollection(); + SocketClientCollection pendingList; while(1) { SocketClientCollection::iterator it; @@ -166,10 +167,12 @@ void SocketListener::runListener() { pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { + // NB: calling out to an other object with mClientsLock held (safe) int fd = (*it)->getSocket(); FD_SET(fd, &read_fds); - if (fd > max) + if (fd > max) { max = fd; + } } pthread_mutex_unlock(&mClientsLock); SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName); @@ -182,8 +185,14 @@ void SocketListener::runListener() { } else if (!rc) continue; - if (FD_ISSET(mCtrlPipe[0], &read_fds)) - break; + if (FD_ISSET(mCtrlPipe[0], &read_fds)) { + char c = CtrlPipe_Shutdown; + TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1)); + if (c == CtrlPipe_Shutdown) { + break; + } + continue; + } if (mListen && FD_ISSET(mSock, &read_fds)) { struct sockaddr addr; socklen_t alen; @@ -205,53 +214,111 @@ void SocketListener::runListener() { } /* Add all active clients to the pending list first */ - pendingList->clear(); + pendingList.clear(); pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { - int fd = (*it)->getSocket(); + SocketClient* c = *it; + // NB: calling out to an other object with mClientsLock held (safe) + int fd = c->getSocket(); if (FD_ISSET(fd, &read_fds)) { - pendingList->push_back(*it); + pendingList.push_back(c); + c->incRef(); } } pthread_mutex_unlock(&mClientsLock); /* Process the pending list, since it is owned by the thread, * there is no need to lock it */ - while (!pendingList->empty()) { + while (!pendingList.empty()) { /* Pop the first item from the list */ - it = pendingList->begin(); + it = pendingList.begin(); SocketClient* c = *it; - pendingList->erase(it); - /* Process it, if false is returned and our sockets are - * connection-based, remove and destroy it */ - if (!onDataAvailable(c) && mListen) { - /* Remove the client from our array */ - SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); - pthread_mutex_lock(&mClientsLock); - for (it = mClients->begin(); it != mClients->end(); ++it) { - if (*it == c) { - mClients->erase(it); - break; - } - } - pthread_mutex_unlock(&mClientsLock); - /* Remove our reference to the client */ - c->decRef(); + pendingList.erase(it); + /* Process it, if false is returned, remove from list */ + if (!onDataAvailable(c)) { + release(c, false); } + c->decRef(); } } - delete pendingList; +} + +bool SocketListener::release(SocketClient* c, bool wakeup) { + bool ret = false; + /* if our sockets are connection-based, remove and destroy it */ + if (mListen && c) { + /* Remove the client from our array */ + SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); + pthread_mutex_lock(&mClientsLock); + SocketClientCollection::iterator it; + for (it = mClients->begin(); it != mClients->end(); ++it) { + if (*it == c) { + mClients->erase(it); + ret = true; + break; + } + } + pthread_mutex_unlock(&mClientsLock); + if (ret) { + ret = c->decRef(); + if (wakeup) { + char b = CtrlPipe_Wakeup; + TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &b, 1)); + } + } + } + return ret; } void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { + SocketClientCollection safeList; + + /* Add all active clients to the safe list first */ + safeList.clear(); pthread_mutex_lock(&mClientsLock); SocketClientCollection::iterator i; for (i = mClients->begin(); i != mClients->end(); ++i) { + SocketClient* c = *i; + c->incRef(); + safeList.push_back(c); + } + pthread_mutex_unlock(&mClientsLock); + + while (!safeList.empty()) { + /* Pop the first item from the list */ + i = safeList.begin(); + SocketClient* c = *i; + safeList.erase(i); // broadcasts are unsolicited and should not include a cmd number - if ((*i)->sendMsg(code, msg, addErrno, false)) { + if (c->sendMsg(code, msg, addErrno, false)) { SLOGW("Error sending broadcast (%s)", strerror(errno)); } + c->decRef(); + } +} + +void SocketListener::runOnEachSocket(SocketClientCommand *command) { + SocketClientCollection safeList; + + /* Add all active clients to the safe list first */ + safeList.clear(); + pthread_mutex_lock(&mClientsLock); + SocketClientCollection::iterator i; + + for (i = mClients->begin(); i != mClients->end(); ++i) { + SocketClient* c = *i; + c->incRef(); + safeList.push_back(c); } pthread_mutex_unlock(&mClientsLock); + + while (!safeList.empty()) { + /* Pop the first item from the list */ + i = safeList.begin(); + SocketClient* c = *i; + safeList.erase(i); + command->runSocketCommand(c); + c->decRef(); + } } |