// TODO: BUG: The Peak on Windows rarely doesn't close the input thread. // This causes the UI thread to hang on exit. void *input_thread(void *_t, struct dll_io_bridge * _io_bridge) { i_bridge = _io_bridge; atransport *t = (atransport *)_t; apacket *p; int active = 0; D("%s: starting transport input thread, reading from fd %d\n", t->serial, t->fd); for(;;){ if(read_packet(t->fd, t->serial, &p)) { D("%s: failed to read apacket from transport on fd %d\n", t->serial, t->fd ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { D("%s: transport SYNC offline\n", t->serial); put_apacket(p); break; } else { if(p->msg.arg1 == t->sync_token) { D("%s: transport SYNC online\n", t->serial); active = 1; } else { D("%s: transport ignoring SYNC %d != %d\n", t->serial, p->msg.arg1, t->sync_token); } } } else { if(active) { D("%s: transport got packet %d, sending to remote\n", t->serial, p->msg.command); t->write_to_remote(p, t); } else { D("%s: transport ignoring packet while offline\n", t->serial); } } put_apacket(p); } // this is necessary to avoid a race condition that occured when a transport closes // while a client socket is still active. D("Pre-close sockets input-thread\n"); close_all_sockets(t); D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd); #ifdef WIN32 kick_transport(t, i_bridge->AdbCloseHandle); #else kick_transport(t, NULL); #endif D("Post-kick transport input-thread\n"); transport_unref(t); D("Post-unref transport input-thread\n"); return 0; }
static void *output_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; ADB_LOGD(ADB_TSPT, "%s: starting transport output thread on fd %d, SYNC online (%d)", t->serial, t->fd, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; if (write_packet(t->fd, t->serial, &p)) { put_apacket(p); ADB_LOGE(ADB_TSPT, "%s: failed to write SYNC packet", t->serial); goto oops; } ADB_LOGD(ADB_TSPT, "%s: data pump started", t->serial); for (;;) { p = get_apacket(); if (t->read_from_remote(p, t) == 0) { ADB_LOGD(ADB_TSPT, "%s: received remote packet, sending to transport", t->serial); if (write_packet(t->fd, t->serial, &p)) { put_apacket(p); ADB_LOGE(ADB_TSPT, "%s: failed to write apacket to transport", t->serial); goto oops; } } else { ADB_LOGE(ADB_TSPT, "%s: remote read failed for transport", t->serial); put_apacket(p); break; } } ADB_LOGD(ADB_TSPT, "%s: SYNC offline for transport", t->serial); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 0; p->msg.arg1 = 0; p->msg.magic = A_SYNC ^ 0xffffffff; if (write_packet(t->fd, t->serial, &p)) { put_apacket(p); ADB_LOGW(ADB_TSPT, "%s: failed to write SYNC apacket to transport", t->serial); } oops: ADB_LOGD(ADB_TSPT, "%s: transport output thread is exiting", t->serial); kick_transport(t); transport_unref(t); return 0; }
static void *input_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; int active = 0; ADB_LOGD(ADB_TSPT, "%s: starting transport input thread, reading from fd %d", t->serial, t->fd); for (;;) { if (read_packet(t->fd, t->serial, &p)) { ADB_LOGE(ADB_TSPT, "%s: failed to read apacket from transport on fd %d", t->serial, t->fd); break; } if (p->msg.command == A_SYNC) { if (p->msg.arg0 == 0) { ADB_LOGE(ADB_TSPT, "%s: transport SYNC offline", t->serial); put_apacket(p); break; } else { if (p->msg.arg1 == t->sync_token) { ADB_LOGD(ADB_TSPT, "%s: transport SYNC online", t->serial); active = 1; } else { ADB_LOGD(ADB_TSPT, "%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token); } } } else { if (active) { ADB_LOGD(ADB_TSPT, "%s: transport got packet, sending to remote", t->serial); t->write_to_remote(p, t); } else { ADB_LOGD(ADB_TSPT, "%s: transport ignoring packet while offline", t->serial); } } put_apacket(p); } // this is necessary to avoid a race condition that occured when a transport closes // while a client socket is still active. close_all_sockets(t); ADB_LOGD(ADB_TSPT, "%s: transport input thread is exiting, fd %d", t->serial, t->fd); kick_transport(t); transport_unref(t); return 0; }
// The transport is opened by transport_register_func before // the read_transport and write_transport threads are started. // // The read_transport thread issues a SYNC(1, token) message to let // the write_transport thread know to start things up. In the event // of transport IO failure, the read_transport thread will post a // SYNC(0,0) message to ensure shutdown. // // The transport will not actually be closed until both threads exit, but the threads // will kick the transport on their way out to disconnect the underlying device. // // read_transport thread reads data from a transport (representing a usb/tcp connection), // and makes the main thread call handle_packet(). static void *read_transport_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; adb_thread_setname(android::base::StringPrintf("<-%s", (t->serial != nullptr ? t->serial : "transport"))); D("%s: starting read_transport thread on fd %d, SYNC online (%d)", t->serial, t->fd, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); D("%s: failed to write SYNC packet", t->serial); goto oops; } D("%s: data pump started", t->serial); for(;;) { p = get_apacket(); if(t->read_from_remote(p, t) == 0){ D("%s: received remote packet, sending to transport", t->serial); if(write_packet(t->fd, t->serial, &p)){ put_apacket(p); D("%s: failed to write apacket to transport", t->serial); goto oops; } } else { D("%s: remote read failed for transport", t->serial); put_apacket(p); break; } } D("%s: SYNC offline for transport", t->serial); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 0; p->msg.arg1 = 0; p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); D("%s: failed to write SYNC apacket to transport", t->serial); } oops: D("%s: read_transport thread is exiting", t->serial); kick_transport(t); transport_unref(t); return 0; }
static void *output_thread(void *_t) { atransport *t = _t; apacket *p; D("from_remote: starting thread for transport %p, on fd %d\n", t, t->fd ); D("from_remote: transport %p SYNC online (%d)\n", t, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, &p)) { put_apacket(p); D("from_remote: failed to write SYNC apacket to transport %p", t); goto oops; } D("from_remote: data pump for transport %p\n", t); for(;;) { p = get_apacket(); if(t->read_from_remote(p, t) == 0){ D("from_remote: received remote packet, sending to transport %p\n", t); if(write_packet(t->fd, &p)){ put_apacket(p); D("from_remote: failed to write apacket to transport %p", t); goto oops; } } else { D("from_remote: remote read failed for transport %p\n", p); put_apacket(p); break; } } D("from_remote: SYNC offline for transport %p\n", t); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 0; p->msg.arg1 = 0; p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, &p)) { put_apacket(p); D("from_remote: failed to write SYNC apacket to transport %p", t); } oops: D("from_remote: thread is exiting for transport %p\n", t); kick_transport(t); transport_unref(t); return 0; }
static int local_socket_enqueue(asocket* s, apacket* p) { D("LS(%d): enqueue %zu", s->id, p->len); p->ptr = p->data; /* if there is already data queue'd, we will receive ** events when it's time to write. just add this to ** the tail */ if (s->pkt_first) { goto enqueue; } /* write as much as we can, until we ** would block or there is an error/eof */ while (p->len > 0) { int r = adb_write(s->fd, p->ptr, p->len); if (r > 0) { p->len -= r; p->ptr += r; continue; } if ((r == 0) || (errno != EAGAIN)) { D("LS(%d): not ready, errno=%d: %s", s->id, errno, strerror(errno)); put_apacket(p); s->has_write_error = true; s->close(s); return 1; /* not ready (error) */ } else { break; } } if (p->len == 0) { put_apacket(p); return 0; /* ready for more data */ } enqueue: p->next = 0; if (s->pkt_first) { s->pkt_last->next = p; } else { s->pkt_first = p; } s->pkt_last = p; /* make sure we are notified when we can drain the queue */ fdevent_add(&s->fde, FDE_WRITE); return 1; /* not ready (backlog) */ }
static void *input_thread(void *_t) { atransport *t = _t; apacket *p; int active = 0; D("to_remote: starting input_thread for %p, reading from fd %d\n", t, t->fd); for(;;){ if(read_packet(t->fd, &p)) { D("to_remote: failed to read apacket from transport %p on fd %d\n", t, t->fd ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { D("to_remote: transport %p SYNC offline\n", t); put_apacket(p); break; } else { if(p->msg.arg1 == t->sync_token) { D("to_remote: transport %p SYNC online\n", t); active = 1; } else { D("to_remote: trandport %p ignoring SYNC %d != %d\n", t, p->msg.arg1, t->sync_token); } } } else { if(active) { D("to_remote: transport %p got packet, sending to remote\n", t); t->write_to_remote(p, t); } else { D("to_remote: transport %p ignoring packet while offline\n", t); } } put_apacket(p); } // this is necessary to avoid a race condition that occured when a transport closes // while a client socket is still active. close_all_sockets(t); D("to_remote: thread is exiting for transport %p, fd %d\n", t, t->fd); kick_transport(t); transport_unref(t); return 0; }
// write_transport thread gets packets sent by the main thread (through send_packet()), // and writes to a transport (representing a usb/tcp connection). static void *write_transport_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; int active = 0; adb_thread_setname(android::base::StringPrintf("->%s", (t->serial != nullptr ? t->serial : "transport"))); D("%s: starting write_transport thread, reading from fd %d", t->serial, t->fd); for(;;){ if(read_packet(t->fd, t->serial, &p)) { D("%s: failed to read apacket from transport on fd %d", t->serial, t->fd ); break; } if(p->msg.command == A_SYNC){ if(p->msg.arg0 == 0) { D("%s: transport SYNC offline", t->serial); put_apacket(p); break; } else { if(p->msg.arg1 == t->sync_token) { D("%s: transport SYNC online", t->serial); active = 1; } else { D("%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token); } } } else { if(active) { D("%s: transport got packet, sending to remote", t->serial); t->write_to_remote(p, t); } else { D("%s: transport ignoring packet while offline", t->serial); } } put_apacket(p); } D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd); kick_transport(t); transport_unref(t); return 0; }
/* The transport is opened by transport_register_func before ** the input and output threads are started. ** ** The output thread issues a SYNC(1, token) message to let ** the input thread know to start things up. In the event ** of transport IO failure, the output thread will post a ** SYNC(0,0) message to ensure shutdown. ** ** The transport will not actually be closed until both ** threads exit, but the input thread will kick the transport ** on its way out to disconnect the underlying device. */ void *output_thread(void *_t, struct dll_io_bridge * _io_bridge) { o_bridge = _io_bridge; atransport *t = (atransport *)_t; apacket *p; D("%s: starting transport output thread on fd %d, SYNC online (%d)\n", t->serial, t->fd, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); D("%s: failed to write SYNC packet\n", t->serial); goto oops; } D("%s: data pump started\n", t->serial); for(;;) { p = get_apacket(); if(t->read_from_remote(p, t) == 0){ D("%s: received remote packet, sending to transport\n", t->serial); if(write_packet(t->fd, t->serial, &p)){ put_apacket(p); D("%s: failed to write apacket to transport\n", t->serial); goto oops; } } else { D("%s: remote read failed for transport\n", t->serial); put_apacket(p); break; } } handle_output_offline(t); oops: #ifdef WIN32 handle_output_oops(t, o_bridge->AdbCloseHandle); #else handle_output_oops(t, NULL); #endif return NULL; }
// be sure to hold the socket list lock when calling this static void local_socket_destroy(asocket* s) { apacket *p, *n; int exit_on_close = s->exit_on_close; D("LS(%d): destroying fde.fd=%d", s->id, s->fde.fd); /* IMPORTANT: the remove closes the fd ** that belongs to this socket */ fdevent_remove(&s->fde); /* dispose of any unwritten data */ for (p = s->pkt_first; p; p = n) { D("LS(%d): discarding %zu bytes", s->id, p->len); n = p->next; put_apacket(p); } remove_socket(s); free(s); if (exit_on_close) { D("local_socket_destroy: exiting"); exit(1); } }
static int jdwp_tracker_enqueue( asocket* s, apacket* p ) { /* you can't write to this socket */ put_apacket(p); s->peer->close(s->peer); return -1; }
static int device_tracker_enqueue( asocket* socket, apacket* p ) { /* you can't read from a device tracker, close immediately */ put_apacket(p); device_tracker_close(socket); return -1; }
static void clear_received_packet() { adb_mutex_lock(&packet_lock); while(received != NULL) { apacket* packet = received; received = received->next; put_apacket(packet); } received = last = NULL; adb_mutex_unlock(&packet_lock); }
static void smart_socket_close(asocket* s) { D("SS(%d): closed", s->id); if (s->pkt_first) { put_apacket(s->pkt_first); } if (s->peer) { s->peer->peer = 0; s->peer->close(s->peer); s->peer = 0; } free(s); }
static void handle_output_offline(atransport * t) { apacket *p; D("%s: SYNC offline for transport\n", t->serial); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 0; p->msg.arg1 = 0; p->msg.magic = A_SYNC ^ 0xffffffff; if(write_packet(t->fd, t->serial, &p)) { put_apacket(p); D("%s: failed to write SYNC apacket to transport", t->serial); } }
static void send_auth_response(uint8_t *token, size_t token_size, atransport *t) { D("Calling send_auth_response\n"); apacket *p = get_apacket(); int ret; ret = adb_auth_sign(t->key, token, token_size, p->data); if (!ret) { D("Error signing the token\n"); put_apacket(p); return; } p->msg.command = A_AUTH; p->msg.arg0 = ADB_AUTH_SIGNATURE; p->msg.data_length = ret; send_packet(p, t); }
static void send_auth_publickey(atransport *t) { D("Calling send_auth_publickey\n"); apacket *p = get_apacket(); int ret; ret = adb_auth_get_userkey(p->data, sizeof(p->data)); if (!ret) { D("Failed to get user public key\n"); put_apacket(p); return; } p->msg.command = A_AUTH; p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY; p->msg.data_length = ret; send_packet(p, t); }
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); }
static void local_socket_event_func(int fd, unsigned ev, void *_s) { asocket *s = _s; D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev); /* put the FDE_WRITE processing before the FDE_READ ** in order to simplify the code. */ if(ev & FDE_WRITE){ //check if in the array bool bFound = false; int i = 0; for(i = 0; i < FD_ARRAY_SIZE; ++i){ if(fd_write_array[i] == fd){ //Do not need to update array bFound = true; break; } } if(bFound == false){ if(fd_write_idx > FD_ARRAY_SIZE - 1){ //Out of range, so we have to print fd directly XLOGD("Too many write adb socket fd=%d, ", fd); }else{ fd_write_array[fd_write_idx] = fd; fd_write_idx++; } } apacket *p; while((p = s->pkt_first) != 0) { while(p->len > 0) { int r = adb_write(fd, p->ptr, p->len); if(r > 0) { p->ptr += r; p->len -= r; write_data += r; continue; } if(r < 0) { /* returning here is ok because FDE_READ will ** be processed in the next iteration loop */ if(errno == EAGAIN) return; if(errno == EINTR) continue; } D(" closing after write because r=%d and errno is %d\n", r, errno); XLOGW(" closing after write because r=%d and errno is %d\n", r, errno); s->close(s); return; } clock_t this_write_time = clock(); double cpu_time_used = ((double) (this_write_time - last_write_time)) / CLOCKS_PER_SEC; if( cpu_time_used > 10.0 ) { char output_log[512]; char tmp[16]; sprintf(output_log, "%s", "adb socket write list "); int i = 0; for(i = 0; i < fd_write_idx; ++ i){ sprintf(tmp, "(%d) ", fd_write_array[i]); strcat(output_log, tmp); //XLOGD("adb socket write fd=%d, ", fd_write_array[i]); fd_write_array[i] = 0; } XLOGD("%s\n", output_log); XLOGD("write_data=%d\n", write_data); last_write_time = this_write_time; write_data = 0; fd_write_idx = 0; } if(p->len == 0) { s->pkt_first = p->next; if(s->pkt_first == 0) s->pkt_last = 0; put_apacket(p); } } /* if we sent the last packet of a closing socket, ** we can now destroy it. */ if (s->closing) { D(" closing because 'closing' is set after write\n"); XLOGW(" closing because 'closing' is set after write\n"); s->close(s); return; } /* no more packets queued, so we can ignore ** writable events again and tell our peer ** to resume writing */ fdevent_del(&s->fde, FDE_WRITE); s->peer->ready(s->peer); } if(ev & FDE_READ){ //check if in the array bool bFound = false; int i = 0; for(i = 0; i < FD_ARRAY_SIZE; ++i){ if(fd_read_array[i] == fd){ //Do not need to update array bFound = true; break; } } if(bFound == false){ if(fd_read_idx > FD_ARRAY_SIZE - 1){ //Out of range, so we have to print directly XLOGD("Too many read adb socket fd=%d, ", fd); }else{ fd_read_array[fd_read_idx] = fd; fd_read_idx++; } } apacket *p = get_apacket(); unsigned char *x = p->data; size_t avail = MAX_PAYLOAD; int r; int is_eof = 0; clock_t time1 = clock(); while(avail > 0) { r = adb_read(fd, x, avail); D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail); if(r > 0) { avail -= r; x += r; read_data += r; continue; } if(r < 0) { if(errno == EAGAIN) break; if(errno == EINTR) continue; } /* r = 0 or unhandled error */ is_eof = 1; break; } clock_t this_read_time = clock(); double cpu_time_used = ((double) (this_read_time - last_read_time)) / CLOCKS_PER_SEC; if( cpu_time_used > 5.0 ) { char output_log[512]; char tmp[16]; sprintf(output_log, "%s", "adb socket read list "); int i = 0; for(i = 0; i < fd_read_idx; ++ i){ sprintf(tmp, "(%d) ", fd_read_array[i]); strcat(output_log, tmp); //XLOGD("adb socket read fd=%d, ", fd_read_array[i]); fd_read_array[i] = 0; } XLOGD("%s\n", output_log); XLOGD("read_data=%d\n", read_data); last_read_time = this_read_time; read_data = 0; fd_read_idx = 0; } D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n", s->id, s->fd, r, is_eof, s->fde.force_eof); clock_t time2 = clock(); if ( time2 - time1 > max_read_time ){ max_read_time = time2 - time1; } if((avail == MAX_PAYLOAD) || (s->peer == 0)) { put_apacket(p); } else { p->len = MAX_PAYLOAD - avail; r = s->peer->enqueue(s->peer, p); D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r); clock_t time3 = clock(); if( time3 - time2 > max_send_time){ max_send_time = time3 - time2; } if(r < 0) { /* error return means they closed us as a side-effect ** and we must return immediately. ** ** note that if we still have buffered packets, the ** socket will be placed on the closing socket list. ** this handler function will be called again ** to process FDE_WRITE events. */ return; } if(r > 0) { /* if the remote cannot accept further events, ** we disable notification of READs. They'll ** be enabled again when we get a call to ready() */ fdevent_del(&s->fde, FDE_READ); } } if( cpu_time_used > 5.0 ) { double read_time = ((double) max_read_time) / CLOCKS_PER_SEC; double send_time = ((double) max_send_time) / CLOCKS_PER_SEC; XLOGD("max_read_time=%f max_send_time=%f \n", read_time, send_time); max_read_time = 0; max_send_time = 0; } /* Don't allow a forced eof if data is still there */ if((s->fde.force_eof && !r) || is_eof) { D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof); XLOGD(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof); s->close(s); } } if(ev & FDE_ERROR){ /* this should be caught be the next read or write ** catching it here means we may skip the last few ** bytes of readable data. */ // s->close(s); D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd); XLOGD("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd); return; } }
// adb.c void handle_packet(apacket *p, atransport *t) { 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: 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(); */ break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ // TODO not support -> drop packet /* if(t->connection_state != CS_OFFLINE) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; } */ break; case A_OKAY: /* READY(local-id, remote-id, "") */ if (id == -1 && p->msg.arg1 == seed) { /* it's first OKAY reply */ adb_mutex_lock(&packet_lock); id = seed; remote_id = p->msg.arg0; D("remote_id %d\n", remote_id); adb_mutex_unlock(&packet_lock); clear_received_packet(); store_received_packet(p); p = NULL; } else if(id == p->msg.arg0 && remote_id == p->msg.arg1) { // TODO check OKAY store_received_packet(p); p = NULL; } /* if(t->connection_state != CS_OFFLINE) { } */ break; case A_CLSE: /* CLOSE(local-id, remote-id, "") */ // TODO handle closing shell if ((p->msg.arg0 == remote_id || p->msg.arg0 == 0) && p->msg.arg1 == id) { store_received_packet(p); p = NULL; } /* if(t->connection_state != CS_OFFLINE) { } */ break; case A_WRTE: /*if(t->connection_state != CS_OFFLINE) */{ if(p->msg.arg0 == remote_id) { if(p->msg.data_length > 0) { store_received_packet(p); p = NULL; } send_ready(t); } } break; default: printf("handle_packet: what is %08x?!\n", p->msg.command); } if (p) { put_apacket(p); } }
static int local_socket_enqueue(asocket *s, apacket *p) { D("LS(%d): enqueue %d\n", s->id, p->len); p->ptr = p->data; /* if there is already data queue'd, we will receive ** events when it's time to write. just add this to ** the tail */ if(s->pkt_first) { goto enqueue; } //check if in the array bool bFound = false; int i = 0; for(i = 0; i < FD_ARRAY_SIZE; ++i){ if(fd_write_array[i] == s->fd){ //Do not need to update array bFound = true; break; } } if(bFound == false){ if(fd_write_idx > FD_ARRAY_SIZE - 1){ //Out of range, so we have to print fd directly XLOGD("Too many write adb socket fd=%d, ", s->fd); }else{ fd_write_array[fd_write_idx] = s->fd; fd_write_idx++; } } /* write as much as we can, until we ** would block or there is an error/eof */ while(p->len > 0) { int r = adb_write(s->fd, p->ptr, p->len); if(r > 0) { p->len -= r; p->ptr += r; write_data += r; continue; } if((r == 0) || (errno != EAGAIN)) { D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) ); XLOGW( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) ); s->close(s); return 1; /* not ready (error) */ } else { break; } } clock_t this_write_time = clock(); double cpu_time_used = ((double) (this_write_time - last_write_time)) / CLOCKS_PER_SEC; if( cpu_time_used > 10.0 ) { char output_log[512]; char tmp[16]; sprintf(output_log, "%s", "adb socket write list "); int i = 0; for(i = 0; i < fd_write_idx; ++ i){ sprintf(tmp, "(%d) ", fd_write_array[i]); strcat(output_log, tmp); //XLOGD("adb socket write fd=%d, ", fd_write_array[i]); fd_write_array[i] = 0; } XLOGD("%s\n", output_log); XLOGD("write_data=%d\n", write_data); last_write_time = this_write_time; write_data = 0; fd_write_idx = 0; } if(p->len == 0) { put_apacket(p); return 0; /* ready for more data */ } enqueue: p->next = 0; if(s->pkt_first) { s->pkt_last->next = p; } else { s->pkt_first = p; } s->pkt_last = p; /* make sure we are notified when we can drain the queue */ fdevent_add(&s->fde, FDE_WRITE); return 1; /* not ready (backlog) */ }
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); }
static int smart_socket_enqueue(asocket* s, apacket* p) { unsigned len; #if ADB_HOST char* service = nullptr; char* serial = nullptr; TransportId transport_id = 0; TransportType type = kTransportAny; #endif D("SS(%d): enqueue %zu", s->id, p->len); if (s->pkt_first == 0) { s->pkt_first = p; s->pkt_last = p; } else { if ((s->pkt_first->len + p->len) > s->get_max_payload()) { D("SS(%d): overflow", s->id); put_apacket(p); goto fail; } memcpy(s->pkt_first->data + s->pkt_first->len, p->data, p->len); s->pkt_first->len += p->len; put_apacket(p); p = s->pkt_first; } /* don't bother if we can't decode the length */ if (p->len < 4) { return 0; } len = unhex(p->data, 4); if ((len < 1) || (len > MAX_PAYLOAD)) { D("SS(%d): bad size (%d)", s->id, len); goto fail; } D("SS(%d): len is %d", s->id, len); /* can't do anything until we have the full header */ if ((len + 4) > p->len) { D("SS(%d): waiting for %zu more bytes", s->id, len + 4 - p->len); return 0; } p->data[len + 4] = 0; D("SS(%d): '%s'", s->id, (char*)(p->data + 4)); #if ADB_HOST service = (char*)p->data + 4; if (!strncmp(service, "host-serial:", strlen("host-serial:"))) { char* serial_end; service += strlen("host-serial:"); // serial number should follow "host:" and could be a host:port string. serial_end = internal::skip_host_serial(service); if (serial_end) { *serial_end = 0; // terminate string serial = service; service = serial_end + 1; } } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) { service += strlen("host-transport-id:"); transport_id = strtoll(service, &service, 10); if (*service != ':') { return -1; } service++; } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) { type = kTransportUsb; service += strlen("host-usb:"); } else if (!strncmp(service, "host-local:", strlen("host-local:"))) { type = kTransportLocal; service += strlen("host-local:"); } else if (!strncmp(service, "host:", strlen("host:"))) { type = kTransportAny; service += strlen("host:"); } else { service = nullptr; } if (service) { asocket* s2; /* some requests are handled immediately -- in that ** case the handle_host_request() routine has sent ** the OKAY or FAIL message and all we have to do ** is clean up. */ if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s) == 0) { /* XXX fail message? */ D("SS(%d): handled host service '%s'", s->id, service); goto fail; } if (!strncmp(service, "transport", strlen("transport"))) { D("SS(%d): okay transport", s->id); p->len = 0; return 0; } /* try to find a local service with this name. ** if no such service exists, we'll fail out ** and tear down here. */ s2 = create_host_service_socket(service, serial, transport_id); if (s2 == 0) { D("SS(%d): couldn't create host service '%s'", s->id, service); SendFail(s->peer->fd, "unknown host service"); goto fail; } /* we've connected to a local host service, ** so we make our peer back into a regular ** local socket and bind it to the new local ** service socket, acknowledge the successful ** connection, and close this smart socket now ** that its work is done. */ SendOkay(s->peer->fd); s->peer->ready = local_socket_ready; s->peer->shutdown = nullptr; s->peer->close = local_socket_close; s->peer->peer = s2; s2->peer = s->peer; s->peer = 0; D("SS(%d): okay", s->id); s->close(s); /* initial state is "ready" */ s2->ready(s2); return 0; } #else /* !ADB_HOST */ if (s->transport == nullptr) { std::string error_msg = "unknown failure"; s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg); if (s->transport == nullptr) { SendFail(s->peer->fd, error_msg); goto fail; } } #endif if (!s->transport) { SendFail(s->peer->fd, "device offline (no transport)"); goto fail; } else if (s->transport->GetConnectionState() == kCsOffline) { /* if there's no remote we fail the connection ** right here and terminate it */ SendFail(s->peer->fd, "device offline (transport offline)"); goto fail; } /* instrument our peer to pass the success or fail ** message back once it connects or closes, then ** detach from it, request the connection, and ** tear down */ s->peer->ready = local_socket_ready_notify; s->peer->shutdown = nullptr; s->peer->close = local_socket_close_notify; s->peer->peer = 0; /* give him our transport and upref it */ s->peer->transport = s->transport; connect_to_remote(s->peer, (char*)(p->data + 4)); s->peer = 0; s->close(s); return 1; fail: /* we're going to close our peer as a side-effect, so ** return -1 to signal that state to the local socket ** who is enqueueing against us */ s->close(s); return -1; }
static void local_socket_event_func(int fd, unsigned ev, void* _s) { asocket* s = reinterpret_cast<asocket*>(_s); D("LS(%d): event_func(fd=%d(==%d), ev=%04x)", s->id, s->fd, fd, ev); /* put the FDE_WRITE processing before the FDE_READ ** in order to simplify the code. */ if (ev & FDE_WRITE) { apacket* p; while ((p = s->pkt_first) != nullptr) { while (p->len > 0) { int r = adb_write(fd, p->ptr, p->len); if (r == -1) { /* returning here is ok because FDE_READ will ** be processed in the next iteration loop */ if (errno == EAGAIN) { return; } } else if (r > 0) { p->ptr += r; p->len -= r; continue; } D(" closing after write because r=%d and errno is %d", r, errno); s->has_write_error = true; s->close(s); return; } if (p->len == 0) { s->pkt_first = p->next; if (s->pkt_first == 0) { s->pkt_last = 0; } put_apacket(p); } } /* if we sent the last packet of a closing socket, ** we can now destroy it. */ if (s->closing) { D(" closing because 'closing' is set after write"); s->close(s); return; } /* no more packets queued, so we can ignore ** writable events again and tell our peer ** to resume writing */ fdevent_del(&s->fde, FDE_WRITE); s->peer->ready(s->peer); } if (ev & FDE_READ) { apacket* p = get_apacket(); char* x = p->data; const size_t max_payload = s->get_max_payload(); size_t avail = max_payload; int r = 0; int is_eof = 0; while (avail > 0) { r = adb_read(fd, x, avail); D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu", s->id, s->fd, r, r < 0 ? errno : 0, avail); if (r == -1) { if (errno == EAGAIN) { break; } } else if (r > 0) { avail -= r; x += r; continue; } /* r = 0 or unhandled error */ is_eof = 1; break; } D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d", s->id, s->fd, r, is_eof, s->fde.force_eof); if ((avail == max_payload) || (s->peer == 0)) { put_apacket(p); } else { p->len = max_payload - avail; // s->peer->enqueue() may call s->close() and free s, // so save variables for debug printing below. unsigned saved_id = s->id; int saved_fd = s->fd; r = s->peer->enqueue(s->peer, p); D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r); if (r < 0) { /* error return means they closed us as a side-effect ** and we must return immediately. ** ** note that if we still have buffered packets, the ** socket will be placed on the closing socket list. ** this handler function will be called again ** to process FDE_WRITE events. */ return; } if (r > 0) { /* if the remote cannot accept further events, ** we disable notification of READs. They'll ** be enabled again when we get a call to ready() */ fdevent_del(&s->fde, FDE_READ); } } /* Don't allow a forced eof if data is still there */ if ((s->fde.force_eof && !r) || is_eof) { D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde.force_eof); s->close(s); return; } } if (ev & FDE_ERROR) { /* this should be caught be the next read or write ** catching it here means we may skip the last few ** bytes of readable data. */ D("LS(%d): FDE_ERROR (fd=%d)", s->id, s->fd); return; } }
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); }
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); }