static void handle_new_connection(atransport* t, apacket* p) { if (t->connection_state != kCsOffline) { t->connection_state = kCsOffline; handle_offline(t); } t->update_version(p->msg.arg0, p->msg.arg1); std::string banner(reinterpret_cast<const char*>(p->data), p->msg.data_length); parse_banner(banner, t); #if ADB_HOST handle_online(t); #else if (!auth_required) { handle_online(t); send_connect(t); } else { send_auth_request(t); } #endif }
void handle_packet(apacket *p, atransport *t) { asocket *s; D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0], ((char*) (&(p->msg.command)))[1], ((char*) (&(p->msg.command)))[2], ((char*) (&(p->msg.command)))[3]); print_packet("recv", p); switch(p->msg.command){ case A_SYNC: if(p->msg.arg0){ send_packet(p, t); if(HOST) send_connect(t); } else { t->connection_state = CS_OFFLINE; handle_offline(t); send_packet(p, t); } return; case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ /* XXX verify version, etc */ if(t->connection_state != CS_OFFLINE) { t->connection_state = CS_OFFLINE; handle_offline(t); } parse_banner((char*) p->data, t); handle_online(); if(!HOST) send_connect(t); break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ if(t->connection_state != CS_OFFLINE) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; s = create_local_service_socket(name); if(s == 0) { send_close(0, p->msg.arg0, t); } else { s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; send_ready(s->id, s->peer->id, t); s->ready(s); } } break; case A_OKAY: /* READY(local-id, remote-id, "") */ if(t->connection_state != CS_OFFLINE) { if((s = find_local_socket(p->msg.arg1))) { if(s->peer == 0) { s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; } s->ready(s); } } break; case A_CLSE: /* CLOSE(local-id, remote-id, "") */ if(t->connection_state != CS_OFFLINE) { if((s = find_local_socket(p->msg.arg1))) { s->close(s); } } break; case A_WRTE: if(t->connection_state != CS_OFFLINE) { if((s = find_local_socket(p->msg.arg1))) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; if(s->enqueue(s, p) == 0) { D("Enqueue the socket\n"); send_ready(s->id, rid, t); } return; } } break; default: printf("handle_packet: what is %08x?!\n", p->msg.command); } put_apacket(p); }
static void read_from_cmd_socket(int sock_fd, int event, void *anything) { CMD_Request rx_message; CMD_Reply tx_message; int status, read_length, expected_length, rx_message_length; int localhost, allowed, log_index; union sockaddr_all where_from; socklen_t from_length; IPAddr remote_ip; unsigned short remote_port, rx_command; struct timeval now, cooked_now; rx_message_length = sizeof(rx_message); from_length = sizeof(where_from); status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0, &where_from.sa, &from_length); if (status < 0) { LOG(LOGS_WARN, LOGF_CmdMon, "Error [%s] reading from control socket %d", strerror(errno), sock_fd); return; } if (from_length > sizeof (where_from) || from_length <= sizeof (where_from.sa.sa_family)) { DEBUG_LOG(LOGF_CmdMon, "Read command packet without source address"); return; } read_length = status; /* Get current time cheaply */ SCH_GetLastEventTime(&cooked_now, NULL, &now); UTI_SockaddrToIPAndPort(&where_from.sa, &remote_ip, &remote_port); /* Check if it's from localhost (127.0.0.1, ::1, or Unix domain) */ switch (remote_ip.family) { case IPADDR_INET4: assert(sock_fd == sock_fd4); localhost = remote_ip.addr.in4 == INADDR_LOOPBACK; break; #ifdef FEAT_IPV6 case IPADDR_INET6: assert(sock_fd == sock_fd6); localhost = !memcmp(remote_ip.addr.in6, &in6addr_loopback, sizeof (in6addr_loopback)); break; #endif case IPADDR_UNSPEC: /* This should be the Unix domain socket */ if (where_from.sa.sa_family != AF_UNIX) return; assert(sock_fd == sock_fdu); localhost = 1; break; default: assert(0); } DEBUG_LOG(LOGF_CmdMon, "Received %d bytes from %s fd %d", status, UTI_SockaddrToString(&where_from.sa), sock_fd); if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) { /* The client is not allowed access, so don't waste any more time on him. Note that localhost is always allowed access regardless of the defined access rules - otherwise, we could shut ourselves out completely! */ return; } if (read_length < offsetof(CMD_Request, data) || read_length < offsetof(CMD_Reply, data) || rx_message.pkt_type != PKT_TYPE_CMD_REQUEST || rx_message.res1 != 0 || rx_message.res2 != 0) { /* We don't know how to process anything like this or an error reply would be larger than the request */ DEBUG_LOG(LOGF_CmdMon, "Command packet dropped"); return; } expected_length = PKL_CommandLength(&rx_message); rx_command = ntohs(rx_message.command); tx_message.version = PROTO_VERSION_NUMBER; tx_message.pkt_type = PKT_TYPE_CMD_REPLY; tx_message.res1 = 0; tx_message.res2 = 0; tx_message.command = rx_message.command; tx_message.reply = htons(RPY_NULL); tx_message.status = htons(STT_SUCCESS); tx_message.pad1 = 0; tx_message.pad2 = 0; tx_message.pad3 = 0; tx_message.sequence = rx_message.sequence; tx_message.pad4 = 0; tx_message.pad5 = 0; if (rx_message.version != PROTO_VERSION_NUMBER) { DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid version (%d != %d)", rx_message.version, PROTO_VERSION_NUMBER); if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) { tx_message.status = htons(STT_BADPKTVERSION); transmit_reply(&tx_message, &where_from); } return; } if (rx_command >= N_REQUEST_TYPES || expected_length < (int)offsetof(CMD_Request, data)) { DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid command %d", rx_command); tx_message.status = htons(STT_INVALID); transmit_reply(&tx_message, &where_from); return; } if (read_length < expected_length) { DEBUG_LOG(LOGF_CmdMon, "Command packet is too short (%d < %d)", read_length, expected_length); tx_message.status = htons(STT_BADPKTLENGTH); transmit_reply(&tx_message, &where_from); return; } /* OK, we have a valid message. Now dispatch on message type and process it. */ log_index = CLG_LogCommandAccess(&remote_ip, &cooked_now); /* Don't reply to all requests from hosts other than localhost if the rate is excessive */ if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) { DEBUG_LOG(LOGF_CmdMon, "Command packet discarded to limit response rate"); return; } if (rx_command >= N_REQUEST_TYPES) { /* This should be already handled */ assert(0); } else { /* Check level of authority required to issue the command. All commands from the Unix domain socket (which is accessible only by the root and chrony user/group) are allowed. */ if (where_from.sa.sa_family == AF_UNIX) { assert(sock_fd == sock_fdu); allowed = 1; } else { switch (permissions[rx_command]) { case PERMIT_AUTH: allowed = 0; break; case PERMIT_LOCAL: allowed = localhost; break; case PERMIT_OPEN: allowed = 1; break; default: assert(0); allowed = 0; } } if (allowed) { switch(rx_command) { case REQ_NULL: /* Do nothing */ break; case REQ_DUMP: handle_dump(&rx_message, &tx_message); break; case REQ_ONLINE: handle_online(&rx_message, &tx_message); break; case REQ_OFFLINE: handle_offline(&rx_message, &tx_message); break; case REQ_BURST: handle_burst(&rx_message, &tx_message); break; case REQ_MODIFY_MINPOLL: handle_modify_minpoll(&rx_message, &tx_message); break; case REQ_MODIFY_MAXPOLL: handle_modify_maxpoll(&rx_message, &tx_message); break; case REQ_MODIFY_MAXDELAY: handle_modify_maxdelay(&rx_message, &tx_message); break; case REQ_MODIFY_MAXDELAYRATIO: handle_modify_maxdelayratio(&rx_message, &tx_message); break; case REQ_MODIFY_MAXDELAYDEVRATIO: handle_modify_maxdelaydevratio(&rx_message, &tx_message); break; case REQ_MODIFY_MAXUPDATESKEW: handle_modify_maxupdateskew(&rx_message, &tx_message); break; case REQ_MODIFY_MAKESTEP: handle_modify_makestep(&rx_message, &tx_message); break; case REQ_LOGON: /* Authentication is no longer supported, log-on always fails */ tx_message.status = htons(STT_FAILED); break; case REQ_SETTIME: handle_settime(&rx_message, &tx_message); break; case REQ_LOCAL2: handle_local(&rx_message, &tx_message); break; case REQ_MANUAL: handle_manual(&rx_message, &tx_message); break; case REQ_N_SOURCES: handle_n_sources(&rx_message, &tx_message); break; case REQ_SOURCE_DATA: handle_source_data(&rx_message, &tx_message); break; case REQ_REKEY: handle_rekey(&rx_message, &tx_message); break; case REQ_ALLOW: handle_allowdeny(&rx_message, &tx_message, 1, 0); break; case REQ_ALLOWALL: handle_allowdeny(&rx_message, &tx_message, 1, 1); break; case REQ_DENY: handle_allowdeny(&rx_message, &tx_message, 0, 0); break; case REQ_DENYALL: handle_allowdeny(&rx_message, &tx_message, 0, 1); break; case REQ_CMDALLOW: handle_cmdallowdeny(&rx_message, &tx_message, 1, 0); break; case REQ_CMDALLOWALL: handle_cmdallowdeny(&rx_message, &tx_message, 1, 1); break; case REQ_CMDDENY: handle_cmdallowdeny(&rx_message, &tx_message, 0, 0); break; case REQ_CMDDENYALL: handle_cmdallowdeny(&rx_message, &tx_message, 0, 1); break; case REQ_ACCHECK: handle_accheck(&rx_message, &tx_message); break; case REQ_CMDACCHECK: handle_cmdaccheck(&rx_message, &tx_message); break; case REQ_ADD_SERVER: handle_add_source(NTP_SERVER, &rx_message, &tx_message); break; case REQ_ADD_PEER: handle_add_source(NTP_PEER, &rx_message, &tx_message); break; case REQ_DEL_SOURCE: handle_del_source(&rx_message, &tx_message); break; case REQ_WRITERTC: handle_writertc(&rx_message, &tx_message); break; case REQ_DFREQ: handle_dfreq(&rx_message, &tx_message); break; case REQ_DOFFSET: handle_doffset(&rx_message, &tx_message); break; case REQ_TRACKING: handle_tracking(&rx_message, &tx_message); break; case REQ_SMOOTHING: handle_smoothing(&rx_message, &tx_message); break; case REQ_SMOOTHTIME: handle_smoothtime(&rx_message, &tx_message); break; case REQ_SOURCESTATS: handle_sourcestats(&rx_message, &tx_message); break; case REQ_RTCREPORT: handle_rtcreport(&rx_message, &tx_message); break; case REQ_TRIMRTC: handle_trimrtc(&rx_message, &tx_message); break; case REQ_CYCLELOGS: handle_cyclelogs(&rx_message, &tx_message); break; case REQ_CLIENT_ACCESSES_BY_INDEX2: handle_client_accesses_by_index(&rx_message, &tx_message); break; case REQ_MANUAL_LIST: handle_manual_list(&rx_message, &tx_message); break; case REQ_MANUAL_DELETE: handle_manual_delete(&rx_message, &tx_message); break; case REQ_MAKESTEP: handle_make_step(&rx_message, &tx_message); break; case REQ_ACTIVITY: handle_activity(&rx_message, &tx_message); break; case REQ_RESELECTDISTANCE: handle_reselect_distance(&rx_message, &tx_message); break; case REQ_RESELECT: handle_reselect(&rx_message, &tx_message); break; case REQ_MODIFY_MINSTRATUM: handle_modify_minstratum(&rx_message, &tx_message); break; case REQ_MODIFY_POLLTARGET: handle_modify_polltarget(&rx_message, &tx_message); break; case REQ_REFRESH: handle_refresh(&rx_message, &tx_message); break; case REQ_SERVER_STATS: handle_server_stats(&rx_message, &tx_message); break; default: DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command); tx_message.status = htons(STT_FAILED); break; } } else { tx_message.status = htons(STT_UNAUTH); } } /* Transmit the response */ { /* Include a simple way to lose one message in three to test resend */ static int do_it=1; if (do_it) { transmit_reply(&tx_message, &where_from); } #if 0 do_it = ((do_it + 1) % 3); #endif } }
void handle_packet(apacket *p, atransport *t) { asocket *s; D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0], ((char*) (&(p->msg.command)))[1], ((char*) (&(p->msg.command)))[2], ((char*) (&(p->msg.command)))[3]); print_packet("recv", p); switch(p->msg.command){ case A_SYNC: if(p->msg.arg0){ send_packet(p, t); if(HOST) send_connect(t); } else { t->connection_state = CS_OFFLINE; handle_offline(t); send_packet(p, t); } return; case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ /* XXX verify version, etc */ if(t->connection_state != CS_OFFLINE) { t->connection_state = CS_OFFLINE; handle_offline(t); } parse_banner(reinterpret_cast<const char*>(p->data), t); if (HOST || !auth_required) { handle_online(t); if (!HOST) send_connect(t); } else { send_auth_request(t); } break; case A_AUTH: if (p->msg.arg0 == ADB_AUTH_TOKEN) { t->connection_state = CS_UNAUTHORIZED; t->key = adb_auth_nextkey(t->key); if (t->key) { send_auth_response(p->data, p->msg.data_length, t); } else { /* No more private keys to try, send the public key */ send_auth_publickey(t); } } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) { if (adb_auth_verify(t->token, p->data, p->msg.data_length)) { adb_auth_verified(t); t->failed_auth_attempts = 0; } else { if (t->failed_auth_attempts++ > 10) adb_sleep_ms(1000); send_auth_request(t); } } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) { adb_auth_confirm_key(p->data, p->msg.data_length, t); } break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; s = create_local_service_socket(name); if(s == 0) { send_close(0, p->msg.arg0, t); } else { s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; send_ready(s->id, s->peer->id, t); s->ready(s); } } break; case A_OKAY: /* READY(local-id, remote-id, "") */ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) { if((s = find_local_socket(p->msg.arg1, 0))) { if(s->peer == 0) { /* On first READY message, create the connection. */ s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; s->ready(s); } else if (s->peer->id == p->msg.arg0) { /* Other READY messages must use the same local-id */ s->ready(s); } else { D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n", p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial); } } } break; case A_CLSE: /* CLOSE(local-id, remote-id, "") or CLOSE(0, remote-id, "") */ if (t->online && p->msg.arg1 != 0) { if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) { /* According to protocol.txt, p->msg.arg0 might be 0 to indicate * a failed OPEN only. However, due to a bug in previous ADB * versions, CLOSE(0, remote-id, "") was also used for normal * CLOSE() operations. * * This is bad because it means a compromised adbd could * send packets to close connections between the host and * other devices. To avoid this, only allow this if the local * socket has a peer on the same transport. */ if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) { D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n", p->msg.arg1, t->serial, s->peer->transport->serial); } else { s->close(s); } } } break; case A_WRTE: /* WRITE(local-id, remote-id, <data>) */ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) { if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; if(s->enqueue(s, p) == 0) { D("Enqueue the socket\n"); send_ready(s->id, rid, t); } return; } } break; default: printf("handle_packet: what is %08x?!\n", p->msg.command); } put_apacket(p); }
void handle_packet(apacket *p, atransport *t) { asocket *s; D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0], ((char*) (&(p->msg.command)))[1], ((char*) (&(p->msg.command)))[2], ((char*) (&(p->msg.command)))[3]); print_packet("recv", p); switch(p->msg.command){ case A_SYNC: if(p->msg.arg0){ send_packet(p, t); if(HOST) send_connect(t); } else { t->connection_state = CS_OFFLINE; handle_offline(t); send_packet(p, t); } return; case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ /* XXX verify version, etc */ if(t->connection_state != CS_OFFLINE) { t->connection_state = CS_OFFLINE; handle_offline(t); } parse_banner((char*) p->data, t); if (HOST || !auth_enabled) { handle_online(t); if(!HOST) send_connect(t); } else { #ifndef NO_AUTH send_auth_request(t); #endif } break; #ifndef NO_AUTH case A_AUTH: if (p->msg.arg0 == ADB_AUTH_TOKEN) { t->key = adb_auth_nextkey(t->key); if (t->key) { send_auth_response(p->data, p->msg.data_length, t); } else { /* No more private keys to try, send the public key */ send_auth_publickey(t); } } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) { if (adb_auth_verify(t->token, p->data, p->msg.data_length)) { adb_auth_verified(t); t->failed_auth_attempts = 0; } else { if (t->failed_auth_attempts++ > 10) adb_sleep_ms(1000); send_auth_request(t); } } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) { adb_auth_confirm_key(p->data, p->msg.data_length, t); } break; #endif case A_OPEN: /* OPEN(local-id, 0, "destination") */ if (t->online) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; s = create_local_service_socket(name); if(s == 0) { send_close(0, p->msg.arg0, t); } else { s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; send_ready(s->id, s->peer->id, t); s->ready(s); } } break; case A_OKAY: /* READY(local-id, remote-id, "") */ if (t->online) { if((s = find_local_socket(p->msg.arg1))) { if(s->peer == 0) { s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; } s->ready(s); } } break; case A_CLSE: /* CLOSE(local-id, remote-id, "") */ if (t->online) { D("CLOSE(%d, %d, \"\")\n", p->msg.arg0, p->msg.arg1); if((s = find_local_socket(p->msg.arg1))) { s->close(s); } } break; case A_WRTE: if (t->online) { if((s = find_local_socket(p->msg.arg1))) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; if(s->enqueue(s, p) == 0) { D("Enqueue the socket\n"); send_ready(s->id, rid, t); } return; } } break; default: printf("handle_packet: what is %08x?!\n", p->msg.command); } put_apacket(p); }
void handle_packet(apacket *p, atransport *t) { D("handle_packet() %c%c%c%c", ((char*) (&(p->msg.command)))[0], ((char*) (&(p->msg.command)))[1], ((char*) (&(p->msg.command)))[2], ((char*) (&(p->msg.command)))[3]); print_packet("recv", p); switch(p->msg.command){ case A_SYNC: if (p->msg.arg0){ send_packet(p, t); #if ADB_HOST send_connect(t); #endif } else { t->connection_state = kCsOffline; handle_offline(t); send_packet(p, t); } return; case A_CNXN: // CONNECT(version, maxdata, "system-id-string") handle_new_connection(t, p); break; case A_AUTH: if (p->msg.arg0 == ADB_AUTH_TOKEN) { t->connection_state = kCsUnauthorized; send_auth_response(p->data, p->msg.data_length, t); } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) { if (adb_auth_verify(t->token, sizeof(t->token), p->data, p->msg.data_length)) { adb_auth_verified(t); t->failed_auth_attempts = 0; } else { if (t->failed_auth_attempts++ > 256) adb_sleep_ms(1000); send_auth_request(t); } } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) { adb_auth_confirm_key(p->data, p->msg.data_length, t); } break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; asocket* s = create_local_service_socket(name, t); if (s == nullptr) { send_close(0, p->msg.arg0, t); } else { s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; send_ready(s->id, s->peer->id, t); s->ready(s); } } break; case A_OKAY: /* READY(local-id, remote-id, "") */ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) { asocket* s = find_local_socket(p->msg.arg1, 0); if (s) { if(s->peer == 0) { /* On first READY message, create the connection. */ s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; s->ready(s); } else if (s->peer->id == p->msg.arg0) { /* Other READY messages must use the same local-id */ s->ready(s); } else { D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s", p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial); } } else { // When receiving A_OKAY from device for A_OPEN request, the host server may // have closed the local socket because of client disconnection. Then we need // to send A_CLSE back to device to close the service on device. send_close(p->msg.arg1, p->msg.arg0, t); } } break; case A_CLSE: /* CLOSE(local-id, remote-id, "") or CLOSE(0, remote-id, "") */ if (t->online && p->msg.arg1 != 0) { asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0); if (s) { /* According to protocol.txt, p->msg.arg0 might be 0 to indicate * a failed OPEN only. However, due to a bug in previous ADB * versions, CLOSE(0, remote-id, "") was also used for normal * CLOSE() operations. * * This is bad because it means a compromised adbd could * send packets to close connections between the host and * other devices. To avoid this, only allow this if the local * socket has a peer on the same transport. */ if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) { D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s", p->msg.arg1, t->serial, s->peer->transport->serial); } else { s->close(s); } } } break; case A_WRTE: /* WRITE(local-id, remote-id, <data>) */ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) { asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0); if (s) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; if (s->enqueue(s, p) == 0) { D("Enqueue the socket"); send_ready(s->id, rid, t); } return; } } break; default: printf("handle_packet: what is %08x?!\n", p->msg.command); } put_apacket(p); }
void * handle_client (void * sd_arg) { int cli_sd; int ret; fd_set fdmask; struct timeval timeout; char * buf; cli_sd = ((struct sd_arg *) sd_arg)->sd; free (sd_arg); while (1) { /* Handle timeouts. */ FD_ZERO(&fdmask); FD_SET(cli_sd, &fdmask); timeout.tv_sec = 0; timeout.tv_usec = TIMEOUT; ret = select (cli_sd + 1, &fdmask, NULL, NULL, &timeout); if (ret == 0) goto done; buf = readline (cli_sd); if (buf == NULL) goto error; if (strcmp (GRP_STAT, buf) == 0) { #ifdef DEBUG printf ("*** HANDLE_CLIENT : cmd %s\n", GRP_STAT); #endif if (handle_grp_stat (cli_sd) != 0) goto error; } else if (strcmp (ONLINE, buf) == 0) { if (handle_online (cli_sd) != 0) goto error; } else if (strcmp (OFFLINE, buf) == 0) { if (handle_offline (cli_sd) != 0) goto error; } else if (strcmp (UPDT, buf) == 0) { #ifdef DEBUG printf ("*** HANDLE_CLIENT : cmd %s\n", UPDT); #endif if (handle_updt (cli_sd) != 0) goto error; } else if (strcmp (ADD_SD, buf) == 0) { #ifdef DEBUG printf ("*** HANDLE_CLIENT : cmd %s\n", ADD_SD); #endif if (handle_add_sd (cli_sd) != 0) goto error; } else if (strcmp (ADD_GRP, buf) == 0) { #ifdef DEBUG printf ("*** HANDLE_CLIENT : cmd %s\n", ADD_GRP); #endif if (handle_add_grp (cli_sd) != 0) goto error; } else if (strcmp (LIST_SD, buf) == 0) { #ifdef DEBUG printf ("*** HANDLE_CLIENT : cmd %s\n", LIST_SD); #endif if (handle_list_sd (cli_sd) != 0) goto error; } else if (strcmp (CREAT_GRP, buf) == 0) { #ifdef DEBUG printf ("*** HANDLE_CLIENT : cmd %s\n", CREAT_GRP); #endif if (handle_creat_grp (cli_sd) != 0) goto error; } else { free (buf); goto done; } free (buf); } error: writeline (cli_sd, ERROR, strlen (ERROR)); #ifdef DEBUG printf ("*** ERROR\n"); #endif close (cli_sd); done: return 0; }
void handle_packet(apacket *p, atransport *t) { asocket *s; D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0], ((char*) (&(p->msg.command)))[1], ((char*) (&(p->msg.command)))[2], ((char*) (&(p->msg.command)))[3]); print_packet("recv", p); switch(p->msg.command){ case A_SYNC: if(p->msg.arg0){ send_packet(p, t); if(HOST) send_connect(t); } else { t->connection_state = CS_OFFLINE; handle_offline(t); send_packet(p, t); } return; case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ /* XXX verify version, etc */ if(t->connection_state != CS_OFFLINE) { t->connection_state = CS_OFFLINE; handle_offline(t); } parse_banner((char*) p->data, t); handle_online(t); send_connect(t); break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; s = create_local_service_socket(name); if(s == 0) { send_close(0, p->msg.arg0, t); } else { s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; send_ready(s->id, s->peer->id, t); s->ready(s); } } break; case A_OKAY: /* READY(local-id, remote-id, "") */ if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) { if((s = find_local_socket(p->msg.arg1, 0))) { if(s->peer == 0) { /* On first READY message, create the connection. */ s->peer = create_remote_socket(p->msg.arg0, t); s->peer->peer = s; s->ready(s); } else if (s->peer->id == p->msg.arg0) { /* Other READY messages must use the same local-id */ s->ready(s); } else { D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n", p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial); } } } break; case A_CLSE: /* CLOSE(local-id, remote-id, "") */ if (t->online && p->msg.arg1 != 0) { if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) { /* According to protocol.txt, p->msg.arg0 might be 0 to indicate * a failed OPEN only. However, due to a bug in previous ADB * versions, CLOSE(0, remote-id, "") was also used for normal * CLOSE() operations. * * This is bad because it means a compromised adbd could * send packets to close connections between the host and * other devices. To avoid this, only allow this if the local * socket has a peer on the same transport. */ if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) { D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n", p->msg.arg1, t->serial, s->peer->transport->serial); } else { s->close(s); } } } break; case A_WRTE: if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) { if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; if(s->enqueue(s, p) == 0) { D("Enqueue the socket\n"); send_ready(s->id, rid, t); } return; } } break; default: printf("handle_packet: what is %08x?!\n", p->msg.command); } put_apacket(p); }