summaryrefslogtreecommitdiffstats
path: root/libsysutils/src
diff options
context:
space:
mode:
Diffstat (limited to 'libsysutils/src')
-rw-r--r--libsysutils/src/FrameworkCommand.cpp5
-rw-r--r--libsysutils/src/FrameworkListener.cpp4
-rw-r--r--libsysutils/src/NetlinkEvent.cpp126
-rw-r--r--libsysutils/src/SocketClient.cpp59
-rw-r--r--libsysutils/src/SocketListener.cpp127
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();
+ }
}