libslack(net) - network module
#include <slack/net.h>
typedef unsigned short port_t; typedef struct sockaddr sockaddr_t;
int net_server(const char *interface, const char *service, port_t port, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize); int net_client(const char *host, const char *service, port_t port, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize); int net_udp_server(const char *interface, const char *service, port_t port, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize); int net_udp_client(const char *host, const char *service, port_t port, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize); int net_create_server(const char *interface, const char *service, port_t port, int type, int protocol, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize); int net_create_client(const char *host, const char *service, port_t port, int type, int protocol, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize); ssize_t net_pack(int sockfd, int flags, const char *fmt, ...); ssize_t net_vpack(int sockfd, int flags, const char *fmt, va_list args); ssize_t net_packto(int sockfd, int flags, const sockaddr_t *to, size_t tolen, const char *fmt, ...); ssize_t net_vpackto(int sockfd, int flags, const sockaddr_t *to, size_t tolen, const char *fmt, va_list args); ssize_t net_unpack(int sockfd, int flags, const char *fmt, ...); ssize_t net_vunpack(int sockfd, int flags, const char *fmt, va_list args); ssize_t net_unpackfrom(int sockfd, int flags, sockaddr_t *from, size_t *fromlen, const char *fmt, ...); ssize_t net_vunpackfrom(int sockfd, int flags, sockaddr_t *from, size_t *fromlen, const char *fmt, va_list args); ssize_t pack(void *buf, size_t size, const char *fmt, ...); ssize_t vpack(void *buf, size_t size, const char *fmt, va_list args); ssize_t unpack(void *buf, size_t size, const char *fmt, ...); ssize_t vunpack(void *buf, size_t size, const char *fmt, va_list args); ssize_t net_read(int sockfd, long timeout, char *buf, size_t count); ssize_t net_write(int sockfd, long timeout, const char *buf, size_t count); ssize_t net_expect(int sockfd, long timeout, const char *fmt, ...); ssize_t net_vexpect(int sockfd, long timeout, const char *fmt, va_list args); ssize_t net_send(int sockfd, long timeout, const char *fmt, ...); ssize_t net_vsend(int sockfd, long timeout, const char *fmt, va_list args); int mail(const char *server, const char *sender, const char *recipients, const char *subject, const char *message);
This module provides functions that create client and server sockets, that expect and send text dialogues, and that pack and unpack arbitrary packets.
int net_server(const char *interface, const char *service, port_t port, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize)
Creates a tcp server socket ready to accept(2) connections on
interface
(as determined by gethostbyname(3)).
If interface
is NULL
, connections will be accepted on all local network interfaces. Otherwise,
connections will only be accepted on the specified interface (as determined
by gethostbyname(3)).
If service
is non-NULL
and maps to a port number (as determined by
getservbyname(3)
), the specified port is used. Otherwise, port
(which must be in host byte order) is used.
If rcvbufsz
is non-zero, the socket's receive buffer size is set to this size. Note
that you may not get the size you request. If this is important, use getsockopt(2) to obtain the actual recieve buffer size.
If sndbufsz
is non-zero, the socket's send buffer size is set to this size. Note that
you may not get the size you ask for. If this is important, use getsockopt(2) to obtain the actual send buffer size.
If addr
is not NULL
, the address bound to is stored at that address. If addrsize
is not NULL
, the length of addr
is stored at that address. On success, returns the new socket's file
descriptor.
On error, returns -1 with errno
set appropriately.
int net_client(const char *host, const char *service, port_t port, long timeout, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize, size_t *addrsize)
Creates a tcp client socket and connects to the server listening at host
(as determined by gethostbyname(3)) on the port number specified by
service
(as determined by getservbyname(3)
) or by port
(which must be in host byte order) otherwise.
If timeout
is non-zero, it specifies the number of seconds after which to timeout the
attempt to connect to the specified server. This can be useful if the
client may attempt to connect to a service that is blocked by a firewall
that drops its packets or if the host you are connecting to does not
protect itself from SYN floods. The native TCP timeouts are very long
(usually minutes) when faced with an unresponsive network and you may not
want your programs to wait that long.
If rcvbufsz
is non-zero, the socket's receive buffer size is set to this size. Note
that you may not get the size you request. If this is important, use getsockopt(2) to obtain the actual recieve buffer size.
If sndbufsz
is non-zero, the socket's send buffer size is set to this size. Note that
you may not get the size you ask for. If this is important, use getsockopt(2) to obtain the actual send buffer size.
If addr
is not NULL
, the address of the peer is stored at that address. If addrsize
is not NULL
, the length of addr
is stored at that address.
On success, returns the new socket's file descriptor. On error, returns -1
with errno
set appropriately.
int net_udp_server(const char *interface, const char *service, port_t port, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize)
Creates a udp server socket ready to recv(2) packets on interface
(as determined by gethostbyname(3)).
If interface
is NULL
, connections will be accepted on all local network interfaces. Otherwise,
connections will only be accepted on the specified interface (as determined
by gethostbyname(3)).
If service
is non-NULL
and maps to a port number (as determined by
getservbyname(3)
), the specified port is used. Otherwise, port
(which must be in host byte order) is used.
If rcvbufsz
is non-zero, the socket's receive buffer size is set to this size. Note
that you may not get the size you request. If this is important, use getsockopt(2) to obtain the actual recieve buffer size.
If sndbufsz
is non-zero, the socket's send buffer size is set to this size. Note that
you may not get the size you ask for. If this is important, use getsockopt(2) to obtain the actual send buffer size.
If addr
is not NULL
, the address bound to is stored at that address. If addrsize
is not NULL
, the length of addr
is stored at that address.
On success, returns the new socket's file descriptor. On error, returns -1
with errno
set appropriately.
int net_udp_client(const char *host, const char *service, port_t port, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize)
Creates a udp client socket and connects to the server listening at host
(as determined by gethostbyname(3)) on the port number specified by
service
(as determined by getservbyname(3)
) or by port
(which must be in host byte order) otherwise.
If rcvbufsz
is non-zero, the socket's receive buffer size is set to this size. Note
that you may not get the size you request. If this is important, use getsockopt(2) to obtain the actual recieve buffer size.
If sndbufsz
is non-zero, the socket's send buffer size is set to this size. Note that
you may not get the size you ask for. If this is important, use getsockopt(2) to obtain the actual send buffer size.
If addr
is not NULL
, the address of the peer is stored at that address. If addrsize
is not NULL
, the length of addr
is stored at that address.
On success, returns the new socket's file descriptor. On error, returns -1
with errno
set appropriately.
int net_create_server(const char *interface, const char *service, port_t port, int type, int protocol, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize)
Creates a server socket of the specified type
(e.g. SOCK_STREAM
or
SOCK_DGRAM
) and protocol
(usually zero) ready to accept(2)
connections on interface
(as determined by gethostbyname(3)).
If interface
is NULL
, connections will be accepted on all local network interfaces. Otherwise,
connections will only be accepted on the specified interface (as determined
by gethostbyname(3)).
If service
is non-NULL
and maps to a port number (as determined by
getservbyname(3)
), the specified port is used. Otherwise, port
(which must be in host byte order) is used.
If rcvbufsz
is non-zero, the socket's receive buffer size is set to this size. Note
that you may not get the size you request. If this is important, use getsockopt(2) to obtain the actual recieve buffer size.
If sndbufsz
is non-zero, the socket's send buffer size is set to this size. Note that
you may not get the size you ask for. If this is important, use getsockopt(2) to obtain the actual send buffer size.
If addr
is not NULL
, the address bound to is stored at that address. If addrsize
is not NULL
, the length of addr
is stored at that address.
On success, returns the new socket's file descriptor. On error, returns -1
with errno
set appropriately.
int net_create_client(const char *host, const char *service, port_t port, int type, int protocol, long timeout, int rcvbufsz, int sndbufsz, sockaddr_t *addr, size_t *addrsize)
Creates a client socket of the specified type
(e.g. SOCK_STREAM
or
SOCK_DGRAM
) and protocol
(usually zero) and connects to the server listening on host
(as determined by gethostbyname(3)) on the port number specified by service
(as determined by getservbyname(3)
) or by
port
(which must be in host byte order) otherwise.
If timeout
is non-zero, it specifies the number of seconds after which to timeout the
attempt to connect to the specified server. This can be useful if the
client may attempt to connect to a service that is blocked by a firewall
that drops its packets or if the host you are connecting to does not
protect itself from SYN floods. The native TCP timeouts are very long
(usually minutes) when faced with an unresponsive network and you may not
want your programs to wait that long.
If rcvbufsz
is non-zero, the socket's receive buffer size is set to this size. Note
that you may not get the size you request. If this is important, use getsockopt(2) to obtain the actual recieve buffer size.
If sndbufsz
is non-zero, the socket's send buffer size is set to this size. Note that
you may not get the size you ask for. If this is important, use getsockopt(2) to obtain the actual send buffer size.
If addr
is not NULL
, the address of the server is stored at that address. If addrsize
is not NULL
, the length of addr
is stored at that address.
On success, returns the new socket's file descriptor. On error, returns -1
with errno
set appropriately.
ssize_t net_pack(int sockfd, int flags, const char *fmt, ...)
Creates a packet containing data packed by pack() as specified by fmt
and sends it on the connected socket, sockfd
, with send(2). flags
is passed to send(2). This is intended for use with UDP. It can work reliably with TCP but only
when the application protocol involves each peer packing and unpacking
alternatively, each waiting for the other's response before making their
next response. On success, returns the number of bytes packed and sent. On
error, returns -1 with errno
set appropriately.
Note, the net_pack() functions can sometimes be inappropriate as they inherently involve copying existing data into a new buffer before writing it. It is much faster to not copy the data at all. When possible, use writev(2) instead to write multiple non-contiguous buffers in a single system call.
ssize_t net_vpack(int sockfd, int flags, const char *fmt, va_list args)
Equivalent to net_pack() with the variable argument list specified directly as for vprintf(3).
ssize_t net_packto(int sockfd, int flags, const sockaddr_t *to, size_t tolen, const char *fmt, ...)
Creates a packet containing data packed by pack() as specified by fmt
and sends it on the unconnected socket, sockfd
, to the address specified by to
with length tolen
with sendto(2). flags is passed to
sendto(2). On success, returns the number of bytes packed and sent. On error,
returns -1 with errno
set appropriately.
ssize_t net_vpackto(int sockfd, int flags, const sockaddr_t *to, size_t tolen, const char *fmt, va_list args)
Equivalent to net_packto() with the variable argument list specified directly as for vprintf(3).
ssize_t net_unpack(int sockfd, int flags, const char *fmt, ...)
Receives a packet of data on the connected socket, sockfd
, with
recv(2), and unpacks it with unpack() as specified by fmt
. flags
is passed to recv(2). On success, returns the number of bytes received and unpacked. On error,
returns -1 with errno
set appropriately.
Note, the net_unpack() functions can sometimes be inappropriate as they inherently involve reading data into a single buffer and then copying it into multiple target buffers. It is much faster to not copy the data at all. When possible, use readv(2) instead to read into multiple non-contiguous buffers in a single system call.
ssize_t net_vunpack(int sockfd, int flags, const char *fmt, va_list args)
Equivalent to net_unpack() with the variable argument list specified directly as for vprintf(3).
ssize_t net_unpackfrom(int sockfd, int flags, sockaddr_t *from, size_t *fromlen, const char *fmt, ...)
Receives a packet of data on the unconnected socket, sockfd
, with
recvfrom(2), and unpacks it with unpack() as specified by fmt
. If
from
is non-NULL
, the source address of the message is stored there.
fromlen
is a value-result parameter, initialized to the size of the
from
buffer, and modified on return to indicate the actual size of the address
stored there. flags is passed to recvfrom(2). On success, returns the number of bytes received and unpacked. On error,
returns -1 with
errno
set appropriately.
ssize_t net_vunpackfrom(int sockfd, int flags, sockaddr_t *from, size_t *fromlen, const char *fmt, va_list args)
Equivalent to net_unpackfrom() with the variable argument list specified directly as for vprintf(3).
ssize_t pack(void *buf, size_t size, const char *fmt, ...)
Packs data into buf
as described by fmt
. The arguments after fmt
contain the data to be packed. size
is the size of buf
. Returns the number of bytes packed on success, or -1 on error with errno
set appropriately.
Note, this is based on the pack() function in perl(1) (in fact, the following documentation is from perlfunc(1)) except that the *
count specifier has different semantics, the ?
count specifier is new, there's no non nul-terminated strings or machine
dependant formats or uuencoding or BER integer compression, everything is
in network byte order, and floats are represented as strings so pack() is suitable for serialising data to be written to disk or sent across a
network to other hosts. OK, v
and w
specifically aren't in network order but sometimes that's needed too.
fmt
can contain the following type specifiers:
a A string with arbitrary binary data z A nul terminated string, will be nul padded b A bit string (rounded out to nearest byte boundary) h A hex string (rounded out to nearest byte boundary) c A char (8 bits) s A short (16 bits) i An int (32 bits) l A long (64 bits - only on some systems) f A single-precision float (length byte + text + nul) d A double-precision float (length byte + text + nul) v A short in "VAX" (little-endian) order (16 bits) w An int in "VAX" (little-endian) order (32 bits) p A pointer (32 bits) x A nul byte X Back up a byte @ Null fill to absolute position
The following rules apply:
Each letter may optionally be followed by a number giving a repeat count or
length, or by "*"
or "?"
. A "*"
will obtain the repeat count or length from the next argument (like printf(3)). The count argument must appear before the first corresponding data
argument. When unpacking "a"
,
"z"
, "b"
or "h"
, a "?"
will obtain the repeat count or length from the size_t
object pointed to by the next argument and the size of the target buffer in
the argument after that. These two arguments must appear before the first
corresponding target buffer argument. This enables unpacking packets that
contain length fields without risking target buffer overflow.
With all types except "a"
, "z"
, "b"
and "h"
the pack()
function will gobble up that many arguments.
The "a"
and "z"
types gobble just one value, but pack it as a string of length count
(specified by the corresponding number), truncating or padding with nuls as
necessary. It is the caller's responsibility to ensure that the data
arguments point to sufficient memory. When unpacking, "z"
strips everything after the first nul, and "a"
returns data verbatim.
Likewise, the "b"
field packs a string that many bits long.
The "h"
field packs a string that many nybbles long.
The "p"
type packs a pointer. You are responsible for ensuring the memory pointed
to is not a temporary value (which can potentially get deallocated before
you get around to using the packed result). A NULL
pointer is created if the corresponding value for "p"
is NULL
. Of course, "p"
is useless if the packed data is to be sent over a network to another
process.
The integer formats "c"
, "s"
, "i"
and "l"
are all on network byte order and so can safely be packed for sending over
a network to another process. However, "l"
rely on a non-ANSI C language feature (namely, the
long long int
type) and so should never be used in portable code, even if it is supported
on the local system. There is no guarantee that a long long packed on one
system will be unpackable on another.
Real numbers (floats and doubles) are packed in text format. Due to the multiplicity of floating formats around, this is done to safely transport real numbers across a network to another process.
It is the caller's responsibility to ensure that there are sufficient
arguments provided to satisfy the requirements of fmt
.
ssize_t vpack(void *buf, size_t size, const char *fmt, va_list args)
Equivalent to pack() with the variable argument list specified directly as for vprintf(3).
ssize_t unpack(void *buf, size_t size, const char *fmt, ...)
Unpacks the data in buf
which was packed by pack(). size
is the size of buf
. fmt
must be equivalent to the fmt
argument to the call to pack() that packed the data. The remaining arguments must be pointers to variables
that will hold the unpacked data or NULL
. If any are NULL
the corresponding data will be skipped (i.e. not unpacked). Unpacked "z"
,
"b"
and "h"
strings are always nul terminated. It is the caller's responsibility to
ensure that the pointers into which these strings are unpacked contain
enough memory (count + 1 bytes). It is the caller's responsibility to
ensure that the non-NULL
pointers into which "a"
strings are unpacked also contain enough memory (count bytes). It is the
caller's responsibility to ensure that there are sufficient arguments
supplied to satisfy the requirements of fmt
, even if they are just
NULL
pointers. Returns the number of bytes unpacked on success, or -1 on error.
ssize_t vunpack(void *buf, size_t size, va_list args)
Equivalent to unpack() with the variable argument list specified directly as for vprintf(3).
ssize_t net_read(int sockfd, long timeout, const char *buf, size_t count)
Repeatedly calls read(2) on the connection oriented socket, sockfd
, until count
bytes have been read into buf
or until EOF is encountered or until it times out (after timeout
seconds). On success, returns the number of bytes read. On error, returns
-1 with errno
set appropriately.
ssize_t net_write(int sockfd, long timeout, const char *buf, size_t count)
Repeatedly calls write(2) on the connection oriented socket, sockfd
, until count
bytes from buf
have been written or until it times out (after timeout
seconds). On success, returns the number of bytes written. On error,
returns -1.
ssize_t net_expect(int sockfd, long timeout, const char *fmt, ...)
Expects and confirms a formatted text message from a remote connection on
the socket, sockfd
. timeout
is the number of seconds to wait before timing out. If timeout
is 0, times out immediately. On success, returns the number of conversions
performed (see scanf(3)). When the connection closes, returns 0. On error, returns -1 with errno
set appropriately.
Note: This is generally unreliable. When TCP segments get lost in transit, the resent bytes can form part of a larger segment so the ``boundaries'' that you may expect in your input can fail to appear. This can lead to lost data (read but not expected). This can only really be used safely when the application protocol involves each peer reading and writing alternatively, each waiting for the other's response before making their next response. In short, net_expect() should only be used in concert with net_send().
ssize_t net_vexpect(int sockfd, long timeout, const char *fmt, va_list args)
Equivalent to net_expect() with the variable argument list specified directly as for vprintf(3).
ssize_t net_send(int sockfd, long timeout, const char *fmt, ...)
Sends a formatted string to a remote connection on the socket, sockfd
.
timeout
is the number of seconds to wait before timing out. On success, returns the
number of bytes written. On error, returns -1 with errno
set appropriately.
ssize_t net_vsend(int sockfd, long timeout, const char *fmt, va_list args)
Equivalent to net_send() with the variable argument list specified directly as for vprintf(3).
int mail(const char *server, const char *sender, const char *recipients, const char *subject, const char *message)
Sends a mail message consisting of subject
and message
from sender
to the addresses in recipients
. recipients
contains mail addresses separated by sequences of comma and/or space
characters. message
must not contain any lines containing only a ``.''
character. On success, returns 0. On error, returns -1 with errno
set appropriately.
MT-Safe except for the net server/client functions which use getprotobynumber(3), getservbyname(3) and gethostbyname(3) which are all non-reentrant. This will be fixed.
These are the errors generated by the functions that return -1 on error. Additional errors may be generated and returned from the underlying system calls. See their manual pages.
gethostbyname(3) failed to identify the host
or interface
argument passed to one of the socket functions.
gethostbyname(3) returned an address from an unsupported address family.
The "l"
format was used with pack() or unpack() when the system doesn't support it or it wasn't compiled into libslack.
A string argument is NULL
.
A pack format count is not a positive integer.
An unpack count or limit argument is not a positive integer.
An argument containing "a"
, "z"
, "b"
or "h"
data to be packed is
NULL
.
An argument containing "b"
data to be packed contains characters outside the range [01].
An argument containing "h"
data to be packed contains characters outside the range [0-9a-fA-F].
An "X"
pack instruction is trying to go back past the start of the packet.
The count argument to an "@"
pack instruction refers to a location before that where the instruction was
encountered (i.e. it's trying to pack leftwards).
The fmt
argument to pack() or unpack() contains an illegal character.
An unpack ?
indirect count argument is NULL
.
A message was too large to be sent with net_send()
.
A packet was too small to store all of the data to be packed or unpacked.
An unpack ?
indirect count argument points to a number greater than the subsequent
limit argument (not enough space in the target buffer).
net_expect() or net_send() timed out.
mail() encountered an error in the dialogue with the SMTP server. This most likely cause of this is a missing or inadequate domain name for the sender address on systems where sendmail(8) requires a real domain name.
A TCP server:
#include <stdio.h> #include <unistd.h> #include <slack/net.h>
void provide_service(int fd) { ... }
int main() { int servfd, clntfd; if ((servfd = net_server(NULL, NULL, 30000, 0, 0, NULL, NULL)) == -1) return 1;
while ((clntfd = accept(servfd, NULL, NULL)) != -1) { pid_t pid; switch (pid = fork()) { case -1: return 1; case 0: provide_service(clntfd); _exit(0); default: close(clntfd); break; } }
return 1; }
A TCP client:
#include <stdio.h> #include <unistd.h> #include <slack/net.h>
void request_service(int fd) { ... }
int main() { int servfd = net_client("localhost", NULL, 30000, 5, 0, 0, NULL, NULL); if (servfd == -1) return 1;
request_service(servfd); close(servfd); return 0; }
A UDP server:
#include <stdio.h> #include <slack/net.h> #include <netinet/in.h>
void provide_service(char *pkt) { ... }
int main() { char pkt[8]; struct sockaddr_in ipaddr; size_t ipsize = sizeof ipaddr; int servfd; if ((servfd = net_udp_server(NULL, NULL, 30000, 0, 0, NULL, NULL)) == -1) return 1;
for (;;) { if (recvfrom(servfd, pkt, 8, 0, (sockaddr_t *)&ipaddr, &ipsize) == -1) return 1;
provide_service(pkt);
if (sendto(servfd, pkt, 8, 0, (sockaddr_t *)&ipaddr, ipsize) == -1) return 1; } }
A UDP client:
#include <stdio.h> #include <unistd.h> #include <slack/net.h>
void request_service(char *pkt) { ... } void receive_service(char *pkt) { ... }
int main() { char pkt[8]; int servfd = net_udp_client("localhost", NULL, 30000, 0, 0, NULL, NULL); if (servfd == -1) return 1;
request_service(pkt);
if (send(servfd, pkt, 8, 0) == -1) return 1;
if (recv(servfd, pkt, 8, 0) == -1) return 1;
receive_service(pkt);
close(servfd); return 0; }
Expect/Send SMTP protocol:
#include <slack/net.h> int tmail(char *sender, char *recipient, char *subject, char *message) { int smtp = net_client("localhost", "smtp", 25, 5, 0, 0, NULL, NULL); int code; int rc = smtp != -1 && net_expect(smtp, 10, "%d", &code) == 1 && code == 220 && net_send(smtp, 10, "HELO %s\r\n", "localhost") != -1 && net_expect(smtp, 10, "%d", &code) == 1 && code == 250 && net_send(smtp, 10, "MAIL FROM: <%s>\r\n", sender) != -1 && net_expect(smtp, 10, "%d", &code) == 1 && code == 250 && net_send(smtp, 10, "RCPT TO: <%s>\r\n", recipient) != -1 && net_expect(smtp, 10, "%d", &code) == 1 && code == 250 && net_send(smtp, 10, "DATA\n") != -1 && net_expect(smtp, 10, "%d", &code) == 1 && code == 354 && net_send(smtp, 10, "From: %s\r\n", sender) != -1 && net_send(smtp, 10, "To: %s\r\n", recipient) != -1 && net_send(smtp, 10, "Subject: %s\r\n", subject) != -1 && net_send(smtp, 10, "\n%s\r\n.\r\n", message) != -1 && net_expect(smtp, 10, "%d", &code) == 1 && code == 250 && net_send(smtp, 10, "QUIT\r\n") != -1 && net_expect(smtp, 10, "%d", &code) == 1 && code == 221;
if (smtp != -1) close(smtp);
return rc; } int main(int ac, char **av) { return !tmail("raf@raf.org", "raf@raf.org", "This is a test", "Are you receiving me?\n"); }
Unpack the size of a gif image:
unsigned short width, height; unpack(gif, 10, "z6v2", NULL, &width, &height);
Pack and unpack a packet with a length field:
char pkt[9], data[5] = "4321"; int packed, unpacked; size_t len;
packed = pack(pkt, sizeof(pkt), "ia*", sizeof(data), sizeof(data), data); unpacked = unpack(pkt, packed, "ia?", &len, &len, sizeof(data), data);
Pack examples from perlfunc(1):
pack(pkt, 4, "cccc", 'A', 'B', 'C', 'D'); // "ABCD" pack(pkt, 4, "c4", 'A', 'B', 'C', 'D'); // "ABCD" pack(pkt, 6, "ccxxcc", 'A', 'B', 'C', 'D'); // "AB\0\0CD" pack(pkt, 4, "s2", 1, 2); // "\0\1\0\2" pack(pkt, 4, "a4", "abcd", "x", "y", "z"); // "abcd" pack(pkt, 4, "aaaa", "abcd", "x", "y", "z"); // "axyz" pack(pkt, 14, "z14", "abcdefg"); // "abcdefg\0\0\0\0\0\0\0"
int bin(const char *bin) { char pkt[4], data[33]; size_t binlen; int ret;
binlen = strlen(bin); memset(data, '0', 32 - binlen); strlcpy(data + 32 - binlen, bin, 33); pack(pkt, 4, "b32", data); unpack(pkt, 4, "i", &ret); return ret; }
int hex(const char *hex) { char pkt[4], data[9]; size_t hexlen; int ret;
hexlen = strlen(hex); memset(data, '0', 8 - hexlen); strlcpy(data + 8 - hexlen, hex, 9); pack(pkt, 4, "h8", data); unpack(pkt, 4, "i", &ret); return ret; }
The server and client functions only support IPv4 addresses (and IPv6 addresses on systems (e.g. Linux) that support IPv6). They probably should support X.25, SNA and UNIX sockets. When creating a server that binds to all local network interfaces, only IPv4 addresses are supported.
When creating a UDP client socket, you can't specify a specific port number to bind(2) to. The system automatically assigns one.
The pack functions assume the following: There are 8 bits in a byte. A char
is 1 byte. A short can be stored in 2 bytes. Integers, long integers and
pointers can be stored in 4 bytes. Long long integers can be stored in 8
bytes. If these datatypes are larger on your system, only the least
significant byte(s)
will be packed.
Packing long long integers is not portable (in ANSI C, anyway).
libslack(3), socket(2), bind(2), listen(2), accept(2), connect(2), shutdown(2), select(2), read(2), write(2), close(2), send(2), sendto(2), recv(2), recvfrom(2), gethostbyname(3), perlfunc(1), fdopen(3), scanf(3), printf(3)
20010215 raf <raf@raf.org>