/* connect: * Set the target address and send the first connection request. This * might not get through of course; later we can just repeat the send * statement because the target address is already set. No need to store * it anywhere. */ static int connect (NET_CONN *conn, const char *target) { int id; static int next_id = 0; struct conn_data_t *data = conn->data; /* The id is a compound of the current time (with 1 second * granularity), and an increasing counter. The time is needed * because the counter resets when you restart the program, and * the counter is needed because of the granularity of the time. */ /* Strictly, this line needs a mutex, but there's nowhere around * here we can call `MUTEX_CREATE'. */ id = (next_id++ << 16) + (time(NULL) & 0xffff); strcpy (data->connect_string, "connect"); data->connect_string[8] = (id >> 24) & 0xff; data->connect_string[9] = (id >> 16) & 0xff; data->connect_string[10] = (id >> 8) & 0xff; data->connect_string[11] = id & 0xff; if (net_assigntarget (data->chan, target)) return 1; if (net_send (data->chan, data->connect_string, 12)) return 2; data->connect_timestamp = __libnet_timer_func(); return 0; }
/* net_init: * Initialises the libnet library. */ int net_init (void) { if (!initialised) { initialised = 1; if (!__libnet_internal__mutex_create) net_set_mutex_funcs (NULL, NULL, NULL, NULL); if (!__libnet_timer_func) net_set_timer_func (NULL); __libnet_timer_func(); detected_drivers = net_driverlist_create(); initialised_drivers = net_driverlist_create(); temp_detected_list = net_driverlist_create(); net_drivers_all = net_driverlist_create(); __libnet_internal__classes_init(); __libnet_internal__drivers_init(); __libnet_internal__channels_init(); __libnet_internal__conns_init(); if (!done_atexit) { if (atexit(exitfunc)) { exitfunc(); return 1; } done_atexit = 1; } } return 0; }
/* get_channel: * Scans the list of connections for one matching this connector, and * fills it in if found, returning positive. Otherwise, adds a new entry * to the list, fills it in, and returns negative. On error, returns zero. */ static int get_channel (struct conns_list *conns, const char *addr, int conn_id, int type, const char *bind, NET_CHANNEL **chan, struct conn_data_t *condat) { #if 0 /* Before we do anything else, time out entries which have been here * too long */ struct conns_list *ptr = conns; while (ptr->next) { if ((unsigned)(__libnet_timer_func() - ptr->last_access_time) > 10000) { struct conns_list *ptr2 = ptr->next; ptr->next = ptr2->next; free (ptr2->addr); free (ptr2); } } #endif while (conns->next) { conns = conns->next; if ((conn_id == conns->client_conn_id) && !strcmp (addr, conns->addr)) { *chan = conns->chan; conns->last_access_time = __libnet_timer_func(); return 1; } } conns->next = malloc (sizeof *conns->next); if (conns->next) { conns->next->next = NULL; conns->next->addr = strdup (addr); conns->next->client_conn_id = conn_id; conns->next->ref = condat; conns->next->last_access_time = __libnet_timer_func(); if (conns->next->addr) { conns->next->chan = net_openchannel (type, bind); if (conns->next->chan) { *chan = conns->next->chan; return -1; } free (conns->next->addr); } free (conns->next); conns->next = NULL; } return 0; }
/* poll_connect: * This function does two things. Firstly it checks for a response from * the server. If there's no response, it then resends the connection * request. * * The possible problem here is that the server's response might just be * delayed. The best way I can see around this problem is for the server * to keep an eye on the return addresses of the connection attempts, and * not open a fresh channel each time a duplicate of an old packet arrives. * This actually kills two birds with one stone, since if either the * client's request packet or the server's response packet are dropped, the * client will eventually resend, causing the server to send an identical * response. * * Later note: In fact we needed to introduce an almost-unique identifier * to pass as well, since the channel's address may be reused later on. */ static int poll_connect (NET_CONN *conn) { struct conn_data_t *data = conn->data; char buffer[8+NET_MAX_ADDRESS_LENGTH]; char addr[NET_MAX_ADDRESS_LENGTH]; if ((net_receive (data->chan, buffer, 8+NET_MAX_ADDRESS_LENGTH, addr) == 8+NET_MAX_ADDRESS_LENGTH) && (!memcmp (buffer, "\0\0\0\0\0\0\0", 8))) { net_fixupaddress_channel(data->chan, &buffer[8], addr); net_assigntarget (data->chan, addr); strcpy (conn->peer_addr, addr); return 1; } /* No response */ { unsigned long clock_value = __libnet_timer_func(); if ((unsigned)(clock_value - data->connect_timestamp) > RESEND_RATE) { net_send (data->chan, data->connect_string, 8); data->connect_timestamp = clock_value; } } return 0; }
/* poll: * This function handles all the true I/O for the RDMs. */ static void poll (NET_CONN *conn) { struct conn_data_t *data = conn->data; /* First check whether anything in the outgoing queue needs sending */ { struct out_packet_t *outp; int clock_value = __libnet_timer_func(), i; for (i = 0; i < MAX_OUTGOING_PACKETS; i++) { outp = data->out.packets + i; if ((outp->data) && (!outp->ack) && ((unsigned)(clock_value - outp->last_send_time) > RESEND_RATE)) { net_send (data->chan, outp->data, outp->size + 4); outp->last_send_time = clock_value; } } } /* Then receive any incoming data */ { int count = 10; /* max num of packets [we're not meant to block] */ unsigned char receive_buffer[16384]; while (count--) { int x; unsigned id; x = net_receive (data->chan, receive_buffer, sizeof receive_buffer, 0); if ((x == 0) || (x == -1)) break; /* quit if no more data or error */ if (x < 4) continue; /* badly formed packet -- no ID */ id = 0; id = (id << 8) + receive_buffer[3]; id = (id << 8) + receive_buffer[2]; id = (id << 8) + receive_buffer[1]; id = (id << 8) + receive_buffer[0]; if (id == 0) { /* it's an acknowledgement */ if (x != 8) continue; /* badly formed */ id = (id << 8) + receive_buffer[7]; id = (id << 8) + receive_buffer[6]; id = (id << 8) + receive_buffer[5]; id = (id << 8) + receive_buffer[4]; if (id >= data->out.base_index) data->out.packets[id % MAX_OUTGOING_PACKETS].ack = 1; if (id == data->out.base_index) { int i, j = id % MAX_OUTGOING_PACKETS; for (i = 0; i < MAX_OUTGOING_PACKETS; i++) { if (!data->out.packets[j].ack || !data->out.packets[j].data) break; data->out.base_index++; free (data->out.packets[j].data); data->out.packets[j].data = NULL; j = (j + 1) % MAX_OUTGOING_PACKETS; } } continue; } if (x == 4) continue; /* zero length */ if (id >= data->in.base_index + MAX_INCOMING_PACKETS) continue; if (id >= data->in.base_index) { struct in_packet_t *ptr = data->in.packets + id % MAX_INCOMING_PACKETS; if (!ptr->data) { /* haven't got this one yet */ ptr->data = malloc (x - 4); if (!ptr->data) continue; memcpy (ptr->data, receive_buffer + 4, x - 4); ptr->size = x - 4; } } /* Acknowledge */ { char buf[8] = { 0 }; memcpy (buf + 4, receive_buffer, 4); net_send (data->chan, buf, 8); } } } }
/* safe_sleep: * This routine acts roughly like `sleep', waiting a certain * number of seconds. It's used because in Linux at least, * normal `sleep' uses SIGALRM in Linux, which we'd rather * not touch (Allegro likes it to be left alone). */ static void safe_sleep (int seconds) { unsigned x = __libnet_timer_func(); int y = 0; while (y < seconds * 1000) y = (unsigned)(__libnet_timer_func() - x); }