/* * Copyright (c) 1990--1997 Apple Computer, Inc. All rights reserved. * * debug_udp.c - UDP layer of remote debug protocol. * * HISTORY * * 17-Dec-97 Klee Dienes * Re-wrote as kdp user library; optionally byte-swap all kdp data * 03-Dec-91 Doug Mitchell * Converted to NRW wire protocol. * 25-04-91 Blaine Garst * eliminated private functions * 07-Jan-90 Doug Mitchell at NeXT * Created. */ #include "kdp-transactions.h" #include "kdp-protocol.h" #include #include "defs.h" #define assert CHECK_FATAL static kdp_return_t kdp_exception_reply (kdp_connection *c, kdp_pkt_t *response) { kdp_return_t kdpret; kdp_exception_ack_t ack; CHECK_FATAL (kdp_is_connected (c)); CHECK_FATAL (kdp_is_bound (c)); ack.hdr.request = KDP_EXCEPTION; ack.hdr.is_reply = 1; ack.hdr.seq = response->exception.hdr.seq; ack.hdr.key = response->exception.hdr.key; kdpret = kdp_transmit_exception (c, (kdp_pkt_t *) &ack); if (kdpret != RR_SUCCESS) { c->logger (KDP_LOG_ERROR, "kdp_exception_reply: unable to acknowledge exception: %s\n", kdp_return_string (kdpret)); } return kdpret; } kdp_return_t kdp_exception_wait (kdp_connection *c, kdp_pkt_t *response, int timeout) { if (c->saved_exception_pending) { memcpy (response, c->saved_exception, KDP_MAX_PACKET_SIZE); c->saved_exception_pending = 0; c->logger (KDP_LOG_DEBUG, "kdp_exception_wait: " "returning previously saved exception (sequence number is %d)", response->hdr.seq); return RR_SUCCESS; } for (;;) { kdp_return_t kdpret; kdpret = kdp_receive_exception (c, response, timeout); if (kdpret != RR_SUCCESS) { return kdpret; } kdpret = kdp_exception_reply (c, response); if (kdpret != RR_SUCCESS) { return kdpret; } if (response->hdr.seq == c->exc_seqno) { c->exc_seqno = (c->exc_seqno + 1) % 256; c->logger (KDP_LOG_DEBUG, "kdp_exception_wait: " "returning previously saved exception (sequence number is %d)", response->hdr.seq); break; } else if (((response->hdr.seq + 1) % 256) == c->exc_seqno) { /* duplicate of previous exception */ c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: " "ignoring duplicate of previous exception (sequence number was %d)\n", response->hdr.seq); continue; } else { c->logger (KDP_LOG_ERROR, "kdp_exception_wait: " "unexpected sequence number for exception (expected %d, got %d)\n", c->exc_seqno, response->hdr.seq); continue; } } return RR_SUCCESS; } kdp_return_t kdp_reply_wait (kdp_connection *c, kdp_pkt_t *response, int timeout) { for (;;) { kdp_return_t kdpret; kdpret = kdp_receive (c, response, timeout); if (kdpret != RR_SUCCESS) { c->logger (KDP_LOG_ERROR, "kdp_reply_wait: error from kdp_receive: %s\n", kdp_return_string (kdpret)); return kdpret; } if (response->hdr.request == KDP_EXCEPTION) { kdpret = kdp_exception_reply (c, response); if (kdpret != RR_SUCCESS) { c->logger (KDP_LOG_ERROR, "kdp_reply_wait: error from kdp_exception_reply: %s\n", kdp_return_string (kdpret)); return kdpret; } if (response->hdr.seq == c->exc_seqno) { c->exc_seqno = (c->exc_seqno + 1) % 256; /* save for future processing */ if (c->saved_exception_pending) { c->logger (KDP_LOG_ERROR, "kdp_reply_wait: " "unable to save exception for future processing; ignoring\n"); c->logger (KDP_LOG_ERROR, "kdp_reply_wait: " "exception had sequence number %d\n", response->hdr.seq); return RR_IP_ERROR; } c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: " "saving exception for future processing (sequence number is %d)\n", response->hdr.seq); memcpy (c->saved_exception, response, KDP_MAX_PACKET_SIZE); c->saved_exception_pending = 1; continue; } else if (((response->hdr.seq + 1) % 256) == c->exc_seqno) { /* duplicate of previous exception */ c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: " "ignoring duplicate of previous exception (sequence number was %d)\n", response->hdr.seq); continue; } else { c->logger (KDP_LOG_ERROR, "kdp_reply_wait: " "unexpected sequence number for exception (expected %d, got %d)\n", c->exc_seqno, response->hdr.seq); continue; } } else { if (response->hdr.seq == c->seqno) { c->seqno = (c->seqno + 1) % 256; /* return reply */ c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: " "returning reply (sequence number is %d)\n", response->hdr.seq); break; } else if (((response->hdr.seq + 1) % 256) == c->exc_seqno) { /* duplicate of previous response */ c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: " "ignoring duplicate of previous reply (sequence number was %d)\n", response->hdr.seq); continue; } else { c->logger (KDP_LOG_ERROR, "kdp_reply_wait: " "unexpected sequence number for reply (expected %d, got %d)\n", c->seqno, response->hdr.seq); continue; } } } return RR_SUCCESS; } kdp_return_t kdp_transaction (kdp_connection *c, kdp_pkt_t *request, kdp_pkt_t *response, char *name) { kdp_return_t rtn; int retries = c->retries; CHECK_FATAL (kdp_is_bound (c)); CHECK_FATAL (request != NULL); CHECK_FATAL (response != NULL); CHECK_FATAL (name != NULL); request->hdr.seq = c->seqno; request->hdr.key = c->session_key; request->hdr.is_reply = 0; rtn = RR_RECV_TIMEOUT; while (retries--) { rtn = kdp_transmit_debug (c, request); if (rtn != RR_SUCCESS) { break; } if (c->timed_out) { rtn = kdp_reply_wait (c, response, 1); if (rtn == RR_RECV_TIMEOUT) { return rtn; } c->logger (KDP_LOG_INFO, "kdp_transaction (%s): " "host responding; continuing transactions\n", name); } else { rtn = kdp_reply_wait (c, response, c->receive_timeout); } if (rtn == RR_RECV_TIMEOUT) { c->logger (KDP_LOG_INFO, "kdp_transaction (%s): transation timed out\n", name); c->logger (KDP_LOG_INFO, "kdp_transaction (%s): re-sending transaction\n", name); continue; } break; } if (rtn == RR_RECV_TIMEOUT) { c->logger (KDP_LOG_INFO, "kdp_transaction (%s): host not responding; will retry\n", name); c->timed_out = 1; return rtn; } if (rtn != RR_SUCCESS) { return rtn; } /* Check for correct session key (except for CONNECT requests). */ if ((request->hdr.request != KDP_CONNECT) && (response->hdr.key != c->session_key)) { c->logger (KDP_LOG_ERROR, "kdp_transaction (%s): invalid session key %d (expected %d)\n", name, response->hdr.key, c->session_key); return RR_BAD_ACK; } if (! response->hdr.is_reply) { c->logger (KDP_LOG_ERROR, "kdp_transaction (%s): " "response was not tagged as a reply packet\n", name); return RR_BAD_ACK; } if (response->hdr.request != request->hdr.request) { c->logger (KDP_LOG_ERROR, "kdp_transaction (%s):" "packet type of request (%d) does not match packet type of reply (%d)\n", name, request->hdr.request, response->hdr.request); return RR_BAD_ACK; } return RR_SUCCESS; } /* Perform connect sequence. Assumes a disconnected state. If a connect ack is lost, the target wedges. That's because all we can do is retransmit the connect request, with a (most likely) wrong session key and a zero sequence number. The target thinks it's connected, so it rejects these requests. */ kdp_return_t kdp_connect (kdp_connection *c) { kdp_return_t ret; CHECK_FATAL (kdp_is_bound (c)); CHECK_FATAL (! kdp_is_connected (c)); /* Do a connect transaction. */ c->request->connect_req.hdr.request = KDP_CONNECT; c->request->connect_req.req_reply_port = c->reqport; c->request->connect_req.exc_note_port = c->excport; strncpy (c->request->connect_req.greeting, "", 64); c->request->connect_req.greeting[63] = '\0'; ret = kdp_transaction (c, c->request, c->response, "remote_connect"); if (ret != RR_SUCCESS) { return ret; } if (c->response->writemem_reply.error) { c->logger (KDP_LOG_ERROR, "kdp_connect: %s\n", kdp_return_string (c->response->connect_reply.error)); return RR_CONNECT; } c->session_key = c->response->hdr.key; c->connected = 1; return RR_SUCCESS; } kdp_return_t kdp_disconnect (kdp_connection *c) { kdp_return_t ret; CHECK_FATAL (kdp_is_connected (c)); c->request->disconnect_req.hdr.request = KDP_DISCONNECT; ret = kdp_transaction (c, c->request, c->response, "kdp_disconnect"); if (ret != RR_SUCCESS) { return ret; } c->connected = 0; return RR_SUCCESS; }