File: net.cc

package info (click to toggle)
atom4 4.1-6
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 656 kB
  • ctags: 947
  • sloc: cpp: 4,461; makefile: 54; sh: 21; perl: 6
file content (292 lines) | stat: -rw-r--r-- 7,204 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
 * Atom-4 Network Protocol utility functions
 * Implementation file
 *
 * $Id: net.cc,v 1.12 2003/04/15 02:48:11 hsteoh Exp hsteoh $
 */

#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "net.h"


#define IS_TERMINATOR(x)	((x)>=0x00 && (x)<=0x1f)



/*
 *
 * CLASS netparser
 *
 */

void netparser::skip_spaces() {
  while (raw[curpos] && raw[curpos]==' ') curpos++;
}

netparser::netparser(char *message) {
  reset();
  if (message) parse(message);
}

netparser::~netparser() {
  reset();
}

void netparser::parse(char *message) {
  reset();				// kill any existing buffers

  // Find real message length (this is to weed out newlines and other crap
  // that we might get from the network socket)
  raw = message;
  for (rlen=0; raw[rlen] && !IS_TERMINATOR(raw[rlen]); rlen++);
  raw[rlen] = '\0';			// terminate message properly

  // Parse first word
  skip_spaces();
  type = next_word();
}

void netparser::reset() {
  raw=type=NULL;
  rlen=curpos=0;
}

char *netparser::next_word() {
  char *word = &raw[curpos];
  int end;

  // Find end of word
  while (raw[curpos] && raw[curpos]!=' ') curpos++;
  end=curpos;

  skip_spaces();			// must do this first, 'cos
					// skip_spaces() won't go past \0
  raw[end] = '\0';			// null-terminate word

  return word;
}

char *netparser::get_rest() {
  char *rest=&raw[curpos];
  curpos=rlen;				// bump curpos to end of buffer
  return rest;
}



/*
 *
 * CLASS netconn
 *
 */

void netconn::scan_for_packets() {
  int i=0;
  int packet_start;

  while (i<rcvbuf_end) {
    if (!truncate) {
      // Skip terminators
      while (i<rcvbuf_end && IS_TERMINATOR(rcvbuf[i])) i++;

      packet_start=i;			// mark start of packet

      // Scan for end of packet
      while (i<rcvbuf_end && !IS_TERMINATOR(rcvbuf[i])) i++;

      if (!IS_TERMINATOR(rcvbuf[i])) {
        if (i==NET_LINE_LIMIT) {
          truncate=1;			// packet too long; enter truncate mode
          rcvbuf[++i]='\0';		// truncate packet
          process_packet(&rcvbuf[packet_start]);
        } else {			// packet is incomplete
          wrap_buffer(packet_start);	// wrap buf to prepare for more data
          return;
        }
      } else {				// got a packet
        rcvbuf[i]='\0';			// null-terminate packet
        if (i > packet_start) {
          process_packet(&rcvbuf[packet_start]); // forward to derived class
        }				// (ignore if 0 length)
      }
    } else {				// truncate mode
      // Ignore everything until we find a terminator
      for (i=0; i<rcvbuf_end && !IS_TERMINATOR(rcvbuf[i]); i++);

      if (IS_TERMINATOR(rcvbuf[i])) {
        // Found terminator; leave truncate mode.
        truncate=0;
      } else {				// no terminator in buffer yet
        wrap_buffer(i);			// throw away everything
        return;
      }
    } // if (!truncate)
  } // while(i<rcvbuf_end)

  // If we reach here, it means we've exhausted the entire buffer. Clear it
  // and start over.
  rcvbuf_end=0;
}

void netconn::wrap_buffer(int start) {
  size_t wrapsize;

  assert(start>=0 && start<=NET_LINE_LIMIT);

  wrapsize = NET_LINE_LIMIT-start;
  if (wrapsize>0) {
    memmove(&rcvbuf[0], &rcvbuf[start], wrapsize*sizeof(char));
  }

  // Update end of buffer pointer
  rcvbuf_end -= start;
}

netconn::netconn(int sockfd, eventloop *eloop, int sendqueue_limit) :
	loop(eloop), sock(sockfd), sendlimit(sendqueue_limit) {
  rcvbuf_end = 0;
  truncate = 0;

  // Register with event loop
  loop->register_handler(eventloop::READER, sock, this);
}

netconn::~netconn() {
  loop->unregister_handler(eventloop::READER, sock);
  if (sendqueue.num_elem() > 0) {
    flush();
    // (flush() should've unregistered the writer after sending last packet)
  }
  close(sock);				// close client socket
fprintf(stderr, "Client socket (%d) closed\n", sock);
}

int netconn::send_packet(const char *fmt, ...) {
  int rc;
  va_list args;

  va_start(args, fmt);
  rc = vsend_packet(fmt, args);
  va_end(args);
  return rc;
}

int netconn::vsend_packet(const char *fmt, va_list args) {
  int i, count;
  char *sendbuf;

  // Don't bother if outgoing queue is full.
  if (sendqueue.num_elem() >= sendlimit) return 0;

  sendbuf = new char[NET_BUFFER_SIZE];
  if (!sendbuf) return 0;		// out of memory

  count=vsnprintf(sendbuf, NET_LINE_LIMIT, fmt, args);
  if (count >= NET_LINE_LIMIT) {
    // packet got truncated at 1024 bytes; append packet terminator.
    sendbuf[NET_LINE_LIMIT]='\0';
    count=NET_LINE_LIMIT;
  }

  // Verify packet validity
  for (i=0; i<count && !IS_TERMINATOR(sendbuf[i]); i++);
  if (i<count) {			// illegal characters in packet
    delete sendbuf;			// discard buffer
    return 0;
  }

  // Queue packet for sending
  if (sendqueue.num_elem()==0) {
fprintf(stderr, "Registering as writer...\n");
    // Register ourselves as a writer now that we have something to write
    loop->register_handler(eventloop::WRITER, sock, this);
  }
  sendqueue.append(sendbuf);

  return 1;
}

void netconn::read_ready(eventloop *src, int fd) {
  int avail_len=NET_LINE_LIMIT - rcvbuf_end;	// don't use NET_BUFFER_SIZE
					// so that we have room for '\0'.
  int len;				// number of bytes received

  assert(fd==sock);
  if (avail_len<=0) return;		// buffer full: nothing to do

  len=recv(sock, &rcvbuf[rcvbuf_end], avail_len, MSG_NOSIGNAL);
  if (len > 0) {
    rcvbuf_end += len;
    scan_for_packets();
  } else {
    // Note: we're assuming that recv() returns 0 bytes if we hit EOF
    if (len==0) {
      disconnected();
    } else {
      // FIXME: handle recv() errors
    }
  }
}

// FIXME: this function sometimes may block, if the system socket buffer is
// for whatever reason smaller than our max packet size. We could use send()'s
// MSG_DONTWAIT option, and keep track of partial buffers. But that's more
// trouble than it's worth for now.
void netconn::write_ready(eventloop *src, int fd) {
  char *packet, *cp;			// [O]
  int count;

//fprintf(stderr, "Write ready...\n");

  assert(fd==sock);
  assert(sendqueue.num_elem() > 0);

  // Retrieve packet for sending
  packet = cp = sendqueue.remove(sendqueue.headp());
fprintf(stderr, "Sending packet: %s\n", packet);
  count = strlen(packet);
  packet[count] = '\n';			// be nice to telnet
  count++;				// +1 to include packet terminator

  do {
    int sent;

    sent = send(sock, cp, count, MSG_NOSIGNAL);
    if (sent>0) {
      count -= sent;
      cp += sent;
    } else {
      if (errno==EPIPE) {
        disconnected();			// handle error state
        delete packet;
        return;
      } else {
        // FIXME: error occurred, throw exception
      }
    }
  } while (count);

  delete [] packet;			// done with packet buffer
  if (sendqueue.num_elem()==0) {
    // If no more pending packets to send, remove ourselves from the event
    // loop to avoid spinning
fprintf(stderr, "No more pending packets: unregistering as writer\n");
    loop->unregister_handler(eventloop::WRITER, sock);
  }
}

void netconn::flush() {
  while (sendqueue.num_elem() > 0) {
    // Simulate write-ready state until all packets are sent
    write_ready(loop, sock);
  }
}