Merge lp:~andrea.corbellini/beeseek/sniffer into lp:beeseek

Proposed by Andrea Corbellini
Status: Merged
Merged at revision: 30
Proposed branch: lp:~andrea.corbellini/beeseek/sniffer
Merge into: lp:beeseek
Diff against target: 581 lines (+528/-0)
10 files modified
.bzrignore (+1/-0)
sniffer/Makefile (+15/-0)
sniffer/include/handler.h (+22/-0)
sniffer/include/parser.h (+14/-0)
sniffer/include/sender.h (+16/-0)
sniffer/include/sniffer.h (+8/-0)
sniffer/src/app.c (+68/-0)
sniffer/src/handler.c (+98/-0)
sniffer/src/parser.c (+162/-0)
sniffer/src/sender.c (+124/-0)
To merge this branch: bzr merge lp:~andrea.corbellini/beeseek/sniffer
Reviewer Review Type Date Requested Status
Lorenzo Allegrucci Approve
BeeSeek Team Pending
Review via email: mp+28393@code.launchpad.net

Description of the change

This branch adds the TCP/IP packets sniffer. It's fully written in C, asynchronous and optimized for low memory usage and light CPU load; also privacy is respected.

First, a quick overview of what it does. Basically, every sniffed packet is checked to see if it looks like a HTTP request. If so, it sends the most important information of the request to the analyzer. Here's how you can test it:

* Build it with `make` or `make DEBUG=1`.
* Launch a script that emulates the analyzer: http://paste.ubuntu.com/454410/
* Launch `beeseek-sniffer` with two options: the Ethernet device (most likely eth0 or wlan0) and the IP/hostname of the analyzer (localhost).
* With the web browser, visit some pages.
* The script that emulates the analyzer should now display all the pages visited (plus CSS, Javascript and images, of course).

Here is a detailed description of how it works:

* The packets sniffed are just the ones with the destination port 80 (HTTP). [include/handler.h: Bf_PCAP_FILTER_EXP]
* When a packet is caught, a parser checks if it starts with "GET" or "HEAD" (we don't care about other methods such as POST or PUT). [src/parser.c: BfHTTPRequest_ReadRequestLine]
* In case of a positive match, it assumes that the packet contains a HTTP request and looks for the URI.
* It then scans all headers looking for 'Host'. [src/parser.c: BfHTTPRequest_ReadHeader]
* If both the URI and the Host have been found, it sends them to the analyzer. [src/sender.c: BfSender_SendReq]

This implementation has however some problems:

* The parser assumes that every HTTP request fits in just one packet. If, for example, the request line and the headers are in two different packets, the request is lost.
* The parser assumes also that for every packet there's just one request. If in a packet there are two or more requests, only the first one is considered.
* Finally, the parser assumes that every HTTP request starts at the beginning of a packet.

These problems may seem critical, however they're not so important. In fact every web browser I've used sends every request in a single packet. Also, fixing the problems above would slow down the application and sightly increase the memory usage.

Although the sniffer application is finished and may be used, it needs some tuning. In include/parser.h the two constants BfHTTPRequest_URL_SIZE and BfHTTPRequest_HOST_SIZE should be set to a reasonable size (currently, URL_SIZE is too small). Also unit tests are missing, but I'll work on them as soon as this branch will be approved.

To post a comment you must log in.
58. By Andrea Corbellini

Ignore HEAD requests.

Revision history for this message
Lorenzo Allegrucci (l-allegrucci) wrote :

The sniffer seems to work here, just increment the buffers for hostname and url to 64 and 2048 bytes, they are too strict.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2010-03-21 12:19:12 +0000
3+++ .bzrignore 2010-06-26 14:49:23 +0000
4@@ -1,1 +1,2 @@
5 beeseek/_version_info.py
6+sniffer/beeseek-sniffer
7
8=== added directory 'sniffer'
9=== added file 'sniffer/Makefile'
10--- sniffer/Makefile 1970-01-01 00:00:00 +0000
11+++ sniffer/Makefile 2010-06-26 14:49:23 +0000
12@@ -0,0 +1,15 @@
13+# Copyright 2010 BeeSeek Developers. This software is licensed under the
14+# GNU Affero General Public License version 3 (see the file LICENSE).
15+
16+ifdef DEBUG
17+ GCC := gcc -g -DBfDebug
18+else
19+ GCC := gcc
20+endif
21+
22+build: beeseek-sniffer
23+beeseek-sniffer:
24+ $(GCC) $(GCC_OPTS) -Wall -Iinclude/ -lpcap -o beeseek-sniffer src/*.c
25+
26+clean:
27+ rm -f beeseek-sniffer
28
29=== added directory 'sniffer/include'
30=== added file 'sniffer/include/handler.h'
31--- sniffer/include/handler.h 1970-01-01 00:00:00 +0000
32+++ sniffer/include/handler.h 2010-06-26 14:49:23 +0000
33@@ -0,0 +1,22 @@
34+/* Copyright 2010 BeeSeek Developers. This software is licensed under the
35+ GNU Affero General Public License version 3 (see the file LICENSE). */
36+
37+#include <pcap.h>
38+
39+typedef struct {
40+ u_int32_t client_addr;
41+ u_int16_t client_port;
42+ u_int32_t server_addr;
43+ u_int16_t server_port;
44+ char *data;
45+ unsigned int data_len;
46+} BfPacket;
47+
48+#define Bf_PCAP_FILTER_EXP "tcp and ip and dst port 80"
49+#define Bf_PCAP_BUF_SIZE 8192
50+
51+BfPacket *BfPacket_New(BfPacket *packet, const u_char *packet_data);
52+char *BfPacket_Repr(BfPacket *packet);
53+void BfSniffer_HandlePacket(u_char *args, const struct pcap_pkthdr *header,
54+ const u_char *packet_data);
55+int BfSniffer_SniffDevice(const char *device_name);
56
57=== added file 'sniffer/include/parser.h'
58--- sniffer/include/parser.h 1970-01-01 00:00:00 +0000
59+++ sniffer/include/parser.h 2010-06-26 14:49:23 +0000
60@@ -0,0 +1,14 @@
61+/* Copyright 2010 BeeSeek Developers. This software is licensed under the
62+ GNU Affero General Public License version 3 (see the file LICENSE). */
63+
64+#define BfHTTPRequest_URL_SIZE 200
65+#define BfHTTPRequest_HOST_SIZE 20
66+
67+typedef struct {
68+ char url[BfHTTPRequest_URL_SIZE+1];
69+ char host[BfHTTPRequest_HOST_SIZE+1];
70+} BfHTTPRequest;
71+
72+int BfHTTPRequest_ParsePacket(BfPacket *packet);
73+int BfHTTPRequest_ReadRequestLine(BfHTTPRequest *request, BfPacket *packet);
74+int BfHTTPRequest_ReadHeader(BfHTTPRequest *request, BfPacket *packet);
75
76=== added file 'sniffer/include/sender.h'
77--- sniffer/include/sender.h 1970-01-01 00:00:00 +0000
78+++ sniffer/include/sender.h 2010-06-26 14:49:23 +0000
79@@ -0,0 +1,16 @@
80+/* Copyright 2010 BeeSeek Developers. This software is licensed under the
81+ GNU Affero General Public License version 3 (see the file LICENSE). */
82+
83+extern int BfSender_Socket;
84+
85+#define BfSender_DEFAULT_PORT 7222
86+
87+int BfSender_Connect(char *address, int port);
88+int BfSender_SendRaw(char *data);
89+int BfSender_SendReq(BfHTTPRequest *request);
90+void BfSender_Close(void);
91+
92+#define BfSender_MSG_CONNECT "PUT /api/sniffer-interface HTTP/1.1\r\n\r\n"
93+#define BfSender_MSG_PAGE_TMPL "http://%s%s\n"
94+#define BfSender_MSG_PAGE_SIZE \
95+ BfHTTPRequest_URL_SIZE + BfHTTPRequest_HOST_SIZE + 8
96
97=== added file 'sniffer/include/sniffer.h'
98--- sniffer/include/sniffer.h 1970-01-01 00:00:00 +0000
99+++ sniffer/include/sniffer.h 2010-06-26 14:49:23 +0000
100@@ -0,0 +1,8 @@
101+/* Copyright 2010 BeeSeek Developers. This software is licensed under the
102+ GNU Affero General Public License version 3 (see the file LICENSE). */
103+
104+extern char *Bf_ProgramName;
105+
106+#include "handler.h"
107+#include "parser.h"
108+#include "sender.h"
109
110=== added directory 'sniffer/src'
111=== added file 'sniffer/src/app.c'
112--- sniffer/src/app.c 1970-01-01 00:00:00 +0000
113+++ sniffer/src/app.c 2010-06-26 14:49:23 +0000
114@@ -0,0 +1,68 @@
115+/* Copyright 2010 BeeSeek Developers. This software is licensed under the
116+ GNU Affero General Public License version 3 (see the file LICENSE). */
117+
118+#include <signal.h>
119+#include <stdio.h>
120+#include <stdlib.h>
121+#include <string.h>
122+#include "sniffer.h"
123+
124+/* The command used to launch the application (i.e. `argv[0]`). */
125+char *Bf_ProgramName;
126+
127+/* Display an help message. */
128+static void
129+Bf_PrintUsage(void)
130+{
131+ printf("Usage: %s DEVICE HOST\n\n", Bf_ProgramName);
132+ printf("Options:\n");
133+ printf(" -h, --help show this help message and exit\n");
134+}
135+
136+/* Handle SIGINT/SIGQUIT. */
137+static void
138+Bf_HandleQuit(int sig)
139+{
140+ BfSender_Close();
141+ printf("%s: sniffer stopped\n", Bf_ProgramName);
142+ exit(0);
143+}
144+
145+/* Application ingress point. */
146+int
147+main(int argc, char **argv)
148+{
149+ /* Read and check the command line arguments. */
150+ Bf_ProgramName = argv[0];
151+ if (argc == 1) {
152+ fprintf(stderr, "%s: error: missing interface name\n", Bf_ProgramName);
153+ Bf_PrintUsage();
154+ return 2;
155+ }
156+ else if (argc == 2) {
157+ if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
158+ Bf_PrintUsage();
159+ return 0;
160+ }
161+ else {
162+ fprintf(stderr, "%s: error: no destination host\n", Bf_ProgramName);
163+ Bf_PrintUsage();
164+ return 2;
165+ }
166+ }
167+ else if (argc > 3) {
168+ fprintf(stderr, "%s: error: too many arguments\n", Bf_ProgramName);
169+ Bf_PrintUsage();
170+ return 2;
171+ }
172+
173+ /* Initialize the sender. */
174+ if (BfSender_Connect(argv[2], BfSender_DEFAULT_PORT) < 0)
175+ return 1;
176+ /* Connect signals. */
177+ signal(SIGINT, Bf_HandleQuit);
178+ signal(SIGQUIT, Bf_HandleQuit);
179+
180+ /* Start sniffing. */
181+ return BfSniffer_SniffDevice(argv[1]);
182+}
183
184=== added file 'sniffer/src/handler.c'
185--- sniffer/src/handler.c 1970-01-01 00:00:00 +0000
186+++ sniffer/src/handler.c 2010-06-26 14:49:23 +0000
187@@ -0,0 +1,98 @@
188+/* Copyright 2010 BeeSeek Developers. This software is licensed under the
189+ GNU Affero General Public License version 3 (see the file LICENSE). */
190+
191+#include <netinet/ip.h>
192+#include <netinet/tcp.h>
193+#include <pcap.h>
194+#include <stdlib.h>
195+#include "sniffer.h"
196+
197+
198+/* Return a BfPacket initialized with the information contained in a packet.
199+ * If the packet doesn't contain data to be parsed, this function returns NULL.
200+ */
201+BfPacket *
202+BfPacket_Init(BfPacket *packet, const u_char *packet_data)
203+{
204+ const struct iphdr *ip;
205+ const struct tcphdr *tcp;
206+
207+ /* Consume the Ethernet header (we don't need it). */
208+ packet_data += 14;
209+ /* Read the IP and TCP headers. */
210+ ip = (struct iphdr *)packet_data;
211+ packet_data += ip->ihl * 4;
212+ tcp = (struct tcphdr *)packet_data;
213+ packet_data += tcp->doff * 4;
214+
215+ if (tcp->psh == 0)
216+ /* This packet has no data. */
217+ return NULL;
218+
219+ /* Populate the BfPacket. */
220+ packet->client_addr = ip->saddr;
221+ packet->client_port = tcp->source;
222+ packet->server_addr = ip->saddr;
223+ packet->server_port = tcp->dest;
224+ packet->data = (char *)packet_data;
225+ packet->data_len = ntohs(ip->tot_len) - ip->ihl * 4 - tcp->doff * 4;
226+
227+ return packet;
228+}
229+
230+/* Return a string representing the sender and the receiver of a packet in a
231+ human-readable form. */
232+char *
233+BfPacket_Repr(BfPacket *packet)
234+{
235+ static char repr[42] = "\0";
236+ sprintf(repr, "%d.%d.%d.%d:%d->%d.%d.%d.%d:%d",
237+ packet->client_addr & 0xFF, (packet->client_addr >> 8) & 0xFF,
238+ (packet->client_addr >> 16) & 0xFF, packet->client_addr >> 24,
239+ ntohs(packet->client_port),
240+ packet->server_addr & 0xFF, (packet->server_addr >> 8) & 0xFF,
241+ (packet->server_addr >> 16) & 0xFF, packet->server_addr >> 24,
242+ ntohs(packet->server_port));
243+ return repr;
244+}
245+
246+/* Handle a packet. */
247+void
248+BfSniffer_HandlePacket(u_char *args, const struct pcap_pkthdr *header,
249+ const u_char *packet_data)
250+{
251+ BfPacket packet;
252+ if (BfPacket_Init(&packet, packet_data) == NULL)
253+ return;
254+#ifdef BfDebug
255+ printf("%s: debug: [%s] new packet with data received\n", Bf_ProgramName,
256+ BfPacket_Repr(&packet));
257+#endif
258+ if (BfHTTPRequest_ParsePacket(&packet) < 0)
259+ exit(1);
260+}
261+
262+/* Start sniffing packets from the device running the PCAP loop. */
263+int
264+BfSniffer_SniffDevice(const char *device_name)
265+{
266+ pcap_t *handler;
267+ char errbuf[PCAP_ERRBUF_SIZE];
268+ struct bpf_program filter_program;
269+
270+ /* Open the Ethernet device. */
271+ handler = pcap_open_live(device_name, Bf_PCAP_BUF_SIZE, 1, 1000, errbuf);
272+ if (handler == NULL) {
273+ fprintf(stderr, "%s: error: cannot open device: %s\n",
274+ Bf_ProgramName, errbuf);
275+ return 1;
276+ }
277+
278+ /* Apply the filter expression. */
279+ pcap_compile(handler, &filter_program, Bf_PCAP_FILTER_EXP, 0, 0);
280+ pcap_setfilter(handler, &filter_program);
281+
282+ /* Handle the packets. */
283+ printf("%s: sniffing\n", Bf_ProgramName);
284+ return pcap_loop(handler, -1, BfSniffer_HandlePacket, NULL);
285+}
286
287=== added file 'sniffer/src/parser.c'
288--- sniffer/src/parser.c 1970-01-01 00:00:00 +0000
289+++ sniffer/src/parser.c 2010-06-26 14:49:23 +0000
290@@ -0,0 +1,162 @@
291+/* Copyright 2010 BeeSeek Developers. This software is licensed under the
292+ GNU Affero General Public License version 3 (see the file LICENSE). */
293+
294+#include <ctype.h>
295+#include <stdio.h>
296+#include <string.h>
297+#include "sniffer.h"
298+
299+/* Parse a BfPacket and send a BfHTTPRequest to the analyzer. */
300+int
301+BfHTTPRequest_ParsePacket(BfPacket *packet)
302+{
303+ /* Here we assume that requests are always at the beginning of a packet.
304+ Although this is not always true, most of the browsers do this, so we
305+ should be able to catch almost every request. */
306+
307+ int status;
308+ BfHTTPRequest request;
309+
310+ if (BfHTTPRequest_ReadRequestLine(&request, packet) < 0)
311+ return 0;
312+
313+ while (packet->data_len > 0) {
314+ status = BfHTTPRequest_ReadHeader(&request, packet);
315+ if (status == -1)
316+ return 0;
317+ else if (status == 0)
318+ continue;
319+ return BfSender_SendReq(&request);
320+ }
321+ return 0;
322+}
323+
324+/* Read the request line and put the parsed data into the given `request`. */
325+int
326+BfHTTPRequest_ReadRequestLine(BfHTTPRequest *request, BfPacket *packet)
327+{
328+ int url_len;
329+ int line_len;
330+
331+ if (packet->data_len < 6)
332+ /* This line is too short to be a request line. */
333+ return -1;
334+
335+ /* Check the request method. */
336+ if (strncmp(packet->data, "GET ", 4) != 0)
337+ /* We don't care about methods other than GET. */
338+ return -1;
339+ packet->data += 4;
340+ packet->data_len -= 4;
341+
342+ /* Get the end of the line. */
343+ line_len = memchr(packet->data, '\n', packet->data_len) -
344+ (void *)packet->data + 1;
345+ if (line_len <= 0)
346+ return -1;
347+
348+ /* Get the URL size. */
349+ url_len = memchr(packet->data, ' ', line_len) - (void *)packet->data;
350+ if (url_len <= 0)
351+ return -1;
352+ else if (url_len >= BfHTTPRequest_URL_SIZE) {
353+ fprintf(stderr,
354+ "%s: error: [%s] URL too long (%d bytes), request discarded\n",
355+ Bf_ProgramName, BfPacket_Repr(packet), url_len);
356+ return -1;
357+ }
358+
359+ /* Put the URL into the request, adding the NULL terminator. */
360+ memcpy(request->url, packet->data, url_len);
361+ request->url[url_len] = '\0';
362+#ifdef BfDebug
363+ printf("%s: debug: [%s] caught request: GET %s\n", Bf_ProgramName,
364+ BfPacket_Repr(packet), request->url);
365+#endif
366+ /* Consume the bytes used. */
367+ packet->data += line_len;
368+ packet->data_len -= line_len;
369+ return 0;
370+}
371+
372+/* Read a header and, if it's Host, put it into the request. */
373+int
374+BfHTTPRequest_ReadHeader(BfHTTPRequest *request, BfPacket *packet)
375+{
376+ char *buf;
377+ char *line_end;
378+ char *host;
379+ char *host_end;
380+ int host_len;
381+
382+ /* TODO Skip lines starting with a space. */
383+
384+ if (packet->data_len < 7)
385+ /* This line is too short to be a Host header (probably headers are
386+ finished). */
387+ return -1;
388+
389+ /* Get the end of the line. */
390+ line_end = memchr(packet->data, '\n', packet->data_len);
391+ if (line_end == NULL)
392+ return -1;
393+ line_end--;
394+
395+ /* Look for the header name-value separator, making the header name lower
396+ case. */
397+ for (buf = packet->data; buf < line_end; buf++) {
398+ if (buf[0] == ':')
399+ break;
400+ buf[0] = tolower(buf[0]);
401+ }
402+ if (buf[0] != ':')
403+ /* This is not a header. */
404+ return -1;
405+
406+ if (strncmp(packet->data, "host", buf - packet->data) != 0) {
407+ /* This is not Host, but an another header. Consume the bytes of the
408+ line and return. */
409+ packet->data_len -= line_end + 2 - packet->data;
410+ packet->data = line_end + 2;
411+ return 0;
412+ }
413+
414+ /* Get the beginning of the host name, skipping any blank space. */
415+ for (buf++ ; buf < line_end; buf++)
416+ if (buf[0] != ' ' && buf[0] != '\t' && buf[0] != '\r') {
417+ host = buf;
418+ break;
419+ }
420+ if (buf == line_end)
421+ /* The header value is not specified, or continues on the next line. */
422+ return 0;
423+
424+ /* Get the end of the host name, skipping the blank spaces. */
425+ for (buf = line_end; buf > host; buf--)
426+ if (buf[0] != ' ' && buf[0] != '\t' && buf[0] != '\r') {
427+ host_end = buf + 1;
428+ break;
429+ }
430+
431+ /* Check if the host name length. */
432+ host_len = host_end - host;
433+ if (host_len > BfHTTPRequest_HOST_SIZE) {
434+ fprintf(stderr,
435+ "%s: error: [%s] host name too long (%d bytes), "
436+ "request discarded\n", Bf_ProgramName, BfPacket_Repr(packet),
437+ host_len);
438+ return -1;
439+ }
440+
441+ /* Put the host name into the request, adding the NULL terminator. */
442+ memcpy(request->host, host, host_len);
443+ request->host[host_len] = '\0';
444+#ifdef BfDebug
445+ printf("%s: debug: [%s] found host: %s\n", Bf_ProgramName,
446+ BfPacket_Repr(packet), request->host);
447+#endif
448+ /* Consume the bytes used. */
449+ packet->data_len -= line_end + 2 - packet->data;
450+ packet->data = line_end + 2;
451+ return 1;
452+}
453
454=== added file 'sniffer/src/sender.c'
455--- sniffer/src/sender.c 1970-01-01 00:00:00 +0000
456+++ sniffer/src/sender.c 2010-06-26 14:49:23 +0000
457@@ -0,0 +1,124 @@
458+/* Copyright 2010 BeeSeek Developers. This software is licensed under the
459+ GNU Affero General Public License version 3 (see the file LICENSE). */
460+
461+#include <errno.h>
462+#include <netdb.h>
463+#include <netinet/in.h>
464+#include <stdio.h>
465+#include <string.h>
466+#include <sys/socket.h>
467+#include <unistd.h>
468+#include "sniffer.h"
469+
470+/* Socket used to send data to the BeeSeek server. */
471+int BfSender_Socket;
472+
473+
474+/* Connect to the BeeSeek server. */
475+int
476+BfSender_Connect(char *address, int port)
477+{
478+ struct hostent *host;
479+ struct sockaddr_in server_addr;
480+
481+#ifdef BfDebug
482+ printf("%s: debug: connecting to server at %s:%d\n", Bf_ProgramName,
483+ address, port);
484+#endif
485+
486+ /* Create the socket. */
487+ BfSender_Socket = socket(AF_INET, SOCK_STREAM, 0);
488+ if (BfSender_Socket < 0) {
489+ fprintf(stderr, "%s: error: cannot create socket: %s\n",
490+ Bf_ProgramName, strerror(errno));
491+ return -1;
492+ }
493+
494+ /* Get the destination host. */
495+ host = gethostbyname(address);
496+ if (host == 0) {
497+ fprintf(stderr, "%s: error: cannot connect to server: %s\n",
498+ Bf_ProgramName, strerror(errno));
499+ return -1;
500+ }
501+
502+ /* Insert the address information. */
503+ memset(&server_addr, 0, sizeof(server_addr));
504+ server_addr.sin_family = AF_INET;
505+ server_addr.sin_addr.s_addr = ((struct in_addr *)(host->h_addr))->s_addr;
506+ server_addr.sin_port = htons(port);
507+
508+ /* Connect to the server. */
509+ if (connect(BfSender_Socket, (struct sockaddr *) &server_addr,
510+ sizeof(server_addr)) < 0) {
511+ fprintf(stderr, "%s: error: cannot connect to server: %s\n",
512+ Bf_ProgramName, strerror(errno));
513+ return -1;
514+ }
515+
516+ /* Initialize the HTTP communication with the server. */
517+ if (BfSender_SendRaw(BfSender_MSG_CONNECT) < 0)
518+ return -1;
519+
520+ char data[12];
521+ int bytes_recvd;
522+ int data_len = 0;
523+
524+ /* Receive the response from the server. */
525+ while (data_len < 12) {
526+ bytes_recvd = recv(BfSender_Socket, data + data_len, 12 - data_len, 0);
527+ if (bytes_recvd < 0) {
528+ fprintf(stderr, "%s: error: cannot get response from server: %s\n",
529+ Bf_ProgramName, strerror(errno));
530+ return -1;
531+ }
532+ else if (bytes_recvd == 0) {
533+ fprintf(stderr, "%s: error: server dropped the connection\n",
534+ Bf_ProgramName);
535+ return -1;
536+ }
537+ data_len += bytes_recvd;
538+ }
539+ /* The status code of the response should be 2XX (e.g. "HTTP/1.1 200 OK").
540+ */
541+ if (data[9] != '2') {
542+ fprintf(stderr, "%s: error: bad response from server: %c%c%c\n",
543+ Bf_ProgramName, data[9], data[10], data[11]);
544+ return -1;
545+ }
546+ return 0;
547+}
548+
549+int
550+BfSender_SendRaw(char *data)
551+{
552+ int bytes_sent;
553+ while (strlen(data) > 0) {
554+ bytes_sent = send(BfSender_Socket, data, strlen(data), MSG_NOSIGNAL);
555+ if (bytes_sent < 0) {
556+ fprintf(stderr, "%s: error: cannot send message to server: %s\n",
557+ Bf_ProgramName, strerror(errno));
558+ return -1;
559+ }
560+ data += bytes_sent;
561+ }
562+ return 0;
563+}
564+
565+int
566+BfSender_SendReq(BfHTTPRequest *request)
567+{
568+ char data[BfSender_MSG_PAGE_SIZE];
569+ sprintf(data, BfSender_MSG_PAGE_TMPL, request->host, request->url);
570+ return BfSender_SendRaw(data);
571+}
572+
573+void
574+BfSender_Close(void)
575+{
576+ shutdown(BfSender_Socket, SHUT_RDWR);
577+ close(BfSender_Socket);
578+#ifdef BfDebug
579+ printf("%s: debug: connection to the server closed\n", Bf_ProgramName);
580+#endif
581+}

Subscribers

People subscribed via source and target branches