4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-03-06 15:06:25 +08:00

Cygwin: AF_UNIX: add winsup/cygwin/socket_tests

This commit is contained in:
Ken Brown 2020-10-26 17:18:30 -04:00
parent 7d9bb0b2bc
commit a4f00ed42f
45 changed files with 3021 additions and 0 deletions

4
.gitignore vendored
View File

@ -7,6 +7,7 @@
.#*
*#
*.exe
*.flt
*.gmo
*.info
@ -42,3 +43,6 @@ core
!core/
lost+found
ename.c.inc
libafunix.a

View File

@ -0,0 +1,52 @@
include Makefile.inc
CFLAGS += -Ilib
AF_UNIX_LIB = libafunix.a
AF_UNIX_HDR = lib/af_unix_hdr.h
EXE = ud_ucase_sv ud_ucase_cl \
us_xfr_cl us_xfr_sv \
us_xfr_v2_cl us_xfr_v2_sv \
scm_cred_recv scm_cred_send \
scm_rights_recv scm_rights_send \
scm_multi_recv scm_multi_send \
us_abstract_bind \
waitall_sv waitall_cl \
readv_socket writev_socket \
msg_peek_sv msg_peek_cl \
fork_socketpair \
select_sv select_cl
all:
cd lib; ${MAKE}
@ ${MAKE} ${EXE}
${EXE}: ${AF_UNIX_LIB} # True as a rough approximation
*.o: ${AF_UNIX_HDR}
scm_cred_recv.o scm_cred_send.o: scm_cred.h
scm_rights_recv.o scm_rights_send.o: scm_rights.h
scm_multi_recv.o scm_multi_send.o: scm_multi.h
us_xfr_sv.o us_xfr_cl.o: us_xfr.h
us_xfr_v2_sv.o us_xfr_v2_cl.o: us_xfr_v2.h
ud_ucase_sv.o ud_ucase_cl.o: ud_ucase.h
waitall_sv.o waitall_cl.o: waitall.h
readv_socket.o writev_socket.o: scatter_gather.h
msg_peek_sv.o msg_peek_cl.o: msg_peek.h
select_sv.o select_cl.o: select_test.h
clean:
cd lib; ${MAKE} clean
${RM} *.exe *.o ${AF_UNIX_LIB}

View File

@ -0,0 +1,11 @@
CC = gcc
CFLAGS = -D_XOPEN_SOURCE=600 \
-D_DEFAULT_SOURCE \
-g -O0 -I. \
-pedantic \
-Wall \
-Wextra \
-Wmissing-prototypes \
-Wno-sign-compare \
-Wno-unused-parameter

View File

@ -0,0 +1,159 @@
0. make
1. Server-client using read/write.
$ cat *.c > a
$ ./us_xfr_sv.exe > b&
$ ./us_xfr_cl.exe < a
$ kill %1
$
[1]+ Terminated ./us_xfr_sv.exe > b
$ diff a b
$ rm a b
Should be able to do same test with v2 versions.
2. Datagram server-client using sendto/recvfrom.
$ ./ud_ucase_sv.exe &
$ ./ud_ucase_cl.exe long message
Server received 4 bytes from /tmp/ud_ucase_cl.925
Response 1: LONG
Server received 7 bytes from /tmp/ud_ucase_cl.925
Response 2: MESSAGE
$ ./ud_ucase_cl.exe 'long message'
Server received 10 bytes from /tmp/ud_ucase_cl.926
Response 1: LONG MESSA
$ kill %1
3. MSG_WAITALL test. In two terminals:
# Terminal 1:
$ ./waitall_sv.exe
# Terminal 2:
$ ./waitall_cl.exe
abcd
abcd
[Should see this echoed in Terminal 1 after both lines have been
typed. Kill both programs with Ctrl-C.]
4. scatter-gather test. In two terminals:
# Terminal 1:
$ ./readv_socket.exe
# Terminal 2:
$ ./writev_socket.exe
wrote 148 bytes
# Terminal 1 should now show:
$ ./readv_socket.exe
read 148 bytes
0: The term buccaneer comes from the word boucan.
1: A boucan is a wooden frame used for cooking meat.
2: Buccaneer is the West Indies name for a pirate.
5. MSG_PEEK test. In two terminals:
# Terminal 1:
$ ./msg_peek_sv.exe
peeking...
# Terminal 2:
$ ./msg_peek_cl.exe
hello
# Terminal 1 should now show:
$ ./msg_peek_sv.exe
peeking...
reading would yield 6 bytes: hello
[After 1 second delay]
reading...
read 6 bytes: hello
[Need to kill msg_peek_cl.]
6. SCM_CREDENTIALS test. In two terminals:
# Terminal 1:
$ ./scm_cred_recv.exe
# Terminal 2:
$ ./scm_cred_send.exe
Sending data = 12345
Send credentials pid=234, uid=197609, gid=197121
sendmsg() returned 4
# Terminal 1 should now show:
$ ./scm_cred_recv.exe
recvmsg() returned 4
Received data = 12345
Received credentials pid=234, uid=197609, gid=197121
Credentials from SO_PEERCRED: pid=234, euid=197609, egid=197121
If use -d option in both programs to use datagrams, the last line
instead reads:
ERROR [EINVAL Invalid argument] getsockopt
I think this is correct. According to
https://man7.org/linux/man-pages/man7/unix.7.html, SO_PEERCRED is
not supported for datagram sockets unless they are created using
socketpair.
But if we use -n in the send program, we get
ERROR [?UNKNOWN? No error] recvmsg
in the recv program. This needs to be fixed. Set appropriate
errno if recvmsg is expecting ancillary data but doesn't get it.
7. fork/socketpair test.
$ ./fork_socketpair.exe
count = 0
count = 1
count = 2
count = 3
count = 4
count = 5
count = 6
count = 7
count = 8
count = 9
8. select test. In two terminals:
# Terminal 1:
$ ./select_sv
waiting for connection request...
# Terminal 2:
$ ./select_cl
waiting for socket to be ready for write...
ready for write, writing until buffer full
buffer full
wrote 262108 bytes
waiting for write ready again...
ready for write, writing once more
wrote 65527 more bytes for a total of 327635
# Terminal 1 should now show:
$ ./select_sv
waiting for connection request...
connection request received; accepting
slowly reading from socket...
read 327635 bytes

View File

@ -0,0 +1,72 @@
/* Adapted from the code for debug/backlog.c in Stevens, Unix Network
Programming. */
#include "af_unix_hdr.h"
int pipefd[2];
#define pfd pipefd[1] /* parent's end */
#define cfd pipefd[0] /* child's end */
/* function prototypes */
void do_parent(void);
void do_child(void);
int
main (int argc, char **argv)
{
pid_t pid;
if (socketpair (AF_UNIX, SOCK_STREAM, 0, pipefd) < 0)
errExit ("socketpair");
if ((pid = fork ()) < 0)
errExit ("fork");
else if (pid == 0)
do_child();
else
do_parent();
}
void
do_parent (void)
{
int count, junk;
if (close (cfd) < 0)
errExit ("close");
for (count = 0; count < 10; count++)
{
printf ("count = %d\n", count);
/* tell child value */
if (write (pfd, &count, sizeof (int)) != sizeof (int))
errExit ("write");
/* wait for child */
if (read (pfd, &junk, sizeof (int)) < 0)
errExit ("read");
sleep (1);
}
count = -1; /* tell child we're all done */
if (write (pfd, &count, sizeof (int)) != sizeof (int))
errExit ("write");
}
void
do_child(void)
{
int count, junk;
if (close (pfd) < 0)
errExit ("close");
/* wait for parent */
if (read (cfd, &count, sizeof (int)) < 0)
errExit ("read");
while (count >= 0)
{
/* tell parent */
if (write (cfd, &junk, sizeof (int)) != sizeof (int))
errExit ("write");
/* wait for parent */
if (read (cfd, &count, sizeof (int)) < 0)
errExit ("read");
}
}

View File

@ -0,0 +1,53 @@
#!/bin/sh
#
# Create a new version of the file ename.c.inc by parsing symbolic
# error names defined in errno.h
#
echo '#include <errno.h>' | cpp -dM |
sed -n -e '/#define *E/s/#define *//p' |sort -k2n |
awk '
BEGIN {
entries_per_line = 4
line_len = 68;
last = 0;
varname =" enames";
print "static char *ename[] = {";
line = " /* 0 */ \"\"";
}
{
if ($2 ~ /^E[A-Z0-9]*$/) { # These entries are sorted at top
synonym[$1] = $2;
} else {
while (last + 1 < $2) {
last++;
line = line ", ";
if (length(line ename) > line_len || last == 1) {
print line;
line = " /* " last " */ ";
line = sprintf(" /* %3d */ ", last);
}
line = line "\"" "\"" ;
}
last = $2;
ename = $1;
for (k in synonym)
if (synonym[k] == $1) ename = ename "/" k;
line = line ", ";
if (length(line ename) > line_len || last == 1) {
print line;
line = " /* " last " */ ";
line = sprintf(" /* %3d */ ", last);;
}
line = line "\"" ename "\"" ;
}
}
END {
print line;
print "};"
print "";
print "#define MAX_ENAME " last;
}
'

View File

@ -0,0 +1,25 @@
include ../Makefile.inc
AF_UNIX_LIB = ../libafunix.a
all: ${AF_UNIX_LIB}
${AF_UNIX_LIB}: *.c ename.c.inc
${CC} -c ${CFLAGS} *.c
${RM} ${AF_UNIX_LIB}
${AR} rs ${AF_UNIX_LIB} *.o
ename.c.inc:
sh Build_ename.sh > ename.c.inc
echo 1>&2 "ename.c.inc built"
*.o: af_unix_hdr.h
error_functions.o: error_functions.h
scm_functions.o: scm_functions.h
unix_sockets.o: unix_sockets.h
clean:
${RM} *.o ename.c.inc ${AF_UNIX_LIB}

View File

@ -0,0 +1,50 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU Lesser General Public License as published *
* by the Free Software Foundation, either version 3 or (at your option) *
* any later version. This program is distributed without any warranty. *
* See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 3-1 */
/* af_unix_hdr.h
Standard header file used by nearly all of our example programs.
*/
#ifndef AF_UNIX_HDR_H
#define AF_UNIX_HDR_H /* Prevent accidental double inclusion */
#include <sys/types.h> /* Type definitions used by many programs */
#include <stdio.h> /* Standard I/O functions */
#include <stdlib.h> /* Prototypes of commonly used library functions,
plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h> /* Prototypes for many system calls */
#include <errno.h> /* Declares errno and defines error constants */
#include <string.h> /* Commonly used string-handling functions */
#include "error_functions.h" /* Declares our error-handling functions */
#include <sys/socket.h>
#include <sys/un.h>
#include "unix_sockets.h" /* Declares our socket functions */
#undef AF_UNIX
#define AF_UNIX 31
#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif
typedef enum { FALSE, TRUE } Boolean;
#define min(m,n) ((m) < (n) ? (m) : (n))
#define max(m,n) ((m) > (n) ? (m) : (n))
#endif

View File

@ -0,0 +1,201 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU Lesser General Public License as published *
* by the Free Software Foundation, either version 3 or (at your option) *
* any later version. This program is distributed without any warranty. *
* See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 3-3 */
/* error_functions.c
Some standard error handling routines used by various programs.
*/
#include <stdarg.h>
#include "error_functions.h"
#include "af_unix_hdr.h"
#include "ename.c.inc" /* Defines ename and MAX_ENAME */
#ifdef __GNUC__ /* Prevent 'gcc -Wall' complaining */
__attribute__ ((__noreturn__)) /* if we call this function as last */
#endif /* statement in a non-void function */
static void
terminate(Boolean useExit3)
{
char *s;
/* Dump core if EF_DUMPCORE environment variable is defined and
is a nonempty string; otherwise call exit(3) or _exit(2),
depending on the value of 'useExit3'. */
s = getenv("EF_DUMPCORE");
if (s != NULL && *s != '\0')
abort();
else if (useExit3)
exit(EXIT_FAILURE);
else
_exit(EXIT_FAILURE);
}
/* Diagnose 'errno' error by:
* outputting a string containing the error name (if available
in 'ename' array) corresponding to the value in 'err', along
with the corresponding error message from strerror(), and
* outputting the caller-supplied error message specified in
'format' and 'ap'. */
static void
outputError(Boolean useErr, int err, Boolean flushStdout,
const char *format, va_list ap)
{
#define BUF_SIZE 500
char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];
vsnprintf(userMsg, BUF_SIZE, format, ap);
if (useErr)
snprintf(errText, BUF_SIZE, " [%s %s]",
(err > 0 && err <= MAX_ENAME) ?
ename[err] : "?UNKNOWN?", strerror(err));
else
snprintf(errText, BUF_SIZE, ":");
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
if (flushStdout)
fflush(stdout); /* Flush any pending stdout */
fputs(buf, stderr);
fflush(stderr); /* In case stderr is not line-buffered */
}
/* Display error message including 'errno' diagnostic, and
return to caller */
void
errMsg(const char *format, ...)
{
va_list argList;
int savedErrno;
savedErrno = errno; /* In case we change it here */
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
errno = savedErrno;
}
/* Display error message including 'errno' diagnostic, and
terminate the process */
void
errExit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Display error message including 'errno' diagnostic, and
terminate the process by calling _exit().
The relationship between this function and errExit() is analogous
to that between _exit(2) and exit(3): unlike errExit(), this
function does not flush stdout and calls _exit(2) to terminate the
process (rather than exit(3), which would cause exit handlers to be
invoked).
These differences make this function especially useful in a library
function that creates a child process that must then terminate
because of an error: the child must terminate without flushing
stdio buffers that were partially filled by the caller and without
invoking exit handlers that were established by the caller. */
void
err_exit(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errno, FALSE, format, argList);
va_end(argList);
terminate(FALSE);
}
/* The following function does the same as errExit(), but expects
the error number in 'errnum' */
void
errExitEN(int errnum, const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(TRUE, errnum, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Print an error message (without an 'errno' diagnostic) */
void
fatal(const char *format, ...)
{
va_list argList;
va_start(argList, format);
outputError(FALSE, 0, TRUE, format, argList);
va_end(argList);
terminate(TRUE);
}
/* Print a command usage error message and terminate the process */
void
usageErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Usage: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}
/* Diagnose an error in command-line arguments and
terminate the process */
void
cmdLineErr(const char *format, ...)
{
va_list argList;
fflush(stdout); /* Flush any pending stdout */
fprintf(stderr, "Command-line usage error: ");
va_start(argList, format);
vfprintf(stderr, format, argList);
va_end(argList);
fflush(stderr); /* In case stderr is not line-buffered */
exit(EXIT_FAILURE);
}

View File

@ -0,0 +1,47 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU Lesser General Public License as published *
* by the Free Software Foundation, either version 3 or (at your option) *
* any later version. This program is distributed without any warranty. *
* See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 3-2 */
/* error_functions.h
Header file for error_functions.c.
*/
#ifndef ERROR_FUNCTIONS_H
#define ERROR_FUNCTIONS_H
/* Error diagnostic routines */
void errMsg(const char *format, ...);
#ifdef __GNUC__
/* This macro stops 'gcc -Wall' complaining that "control reaches
end of non-void function" if we use the following functions to
terminate main() or some other non-void function. */
#define NORETURN __attribute__ ((__noreturn__))
#else
#define NORETURN
#endif
void errExit(const char *format, ...) NORETURN ;
void err_exit(const char *format, ...) NORETURN ;
void errExitEN(int errnum, const char *format, ...) NORETURN ;
void fatal(const char *format, ...) NORETURN ;
void usageErr(const char *format, ...) NORETURN ;
void cmdLineErr(const char *format, ...) NORETURN ;
#endif

View File

@ -0,0 +1,146 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU Lesser General Public License as published *
* by the Free Software Foundation, either version 3 or (at your option) *
* any later version. This program is distributed without any warranty. *
* See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_functions.c
Functions to exchange ancillary data over UNIX domain sockets.
These functions are simplistic, in that they ignore the "real" data
content on the assumption that the sockets are being used only for
the purposes of exchanging ancillary data. In many real-world
applications, the application makes use of both the "real" data
channel and the ancillary data, with some kind of protocol that
determines how the "real" and ancillary data are used together.
*/
#include "scm_functions.h"
/* Send the file descriptor 'fd' over the connected UNIX domain
socket 'sockfd' */
int
sendfd(int sockfd, int fd)
{
struct msghdr msgh;
struct iovec iov;
int data;
struct cmsghdr *cmsgp;
/* Allocate a char array of suitable size to hold the ancillary data.
However, since this buffer is in reality a 'struct cmsghdr', use a
union to ensure that it is aligned as required for that structure.
Alternatively, we could allocate the buffer using malloc(), which
returns a buffer that satisfies the strictest alignment requirements
of any type. However, if we employ that approach, we must ensure
that we free() the buffer on all return paths from this function. */
union {
char buf[CMSG_SPACE(sizeof(int))];
/* Space large enough to hold an 'int' */
struct cmsghdr align;
} controlMsg;
/* The 'msg_name' field can be used to specify the address of the
destination socket when sending a datagram. However, we do not
need to use this field because we presume that 'sockfd' is a
connected socket. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* On Linux, we must transmit at least one byte of real data in
order to send ancillary data. We transmit an arbitrary integer
whose value is ignored by recvfd(). */
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
data = 12345;
/* Set 'msghdr' fields that describe ancillary data */
msgh.msg_control = controlMsg.buf;
msgh.msg_controllen = sizeof(controlMsg.buf);
/* Set up ancillary data describing file descriptor to send */
cmsgp = CMSG_FIRSTHDR(&msgh);
cmsgp->cmsg_level = SOL_SOCKET;
cmsgp->cmsg_type = SCM_RIGHTS;
cmsgp->cmsg_len = CMSG_LEN(sizeof(int));
*((int *) CMSG_DATA(cmsgp)) = fd;
/* Send real plus ancillary data */
if (sendmsg(sockfd, &msgh, 0) == -1)
return -1;
return 0;
}
/* Receive a file descriptor on a connected UNIX domain socket.
The received file descriptor is returned as the function result. */
int
recvfd(int sockfd)
{
struct msghdr msgh;
struct iovec iov;
int data;
ssize_t nr;
/* Allocate a char buffer for the ancillary data. See the comments
in sendfd() */
union {
char buf[CMSG_SPACE(sizeof(int))];
struct cmsghdr align;
} controlMsg;
struct cmsghdr *cmsgp;
/* The 'msg_name' field can be used to obtain the address of the
sending socket. However, we do not need this information. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* Specify buffer for receiving real data */
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data; /* Real data is an 'int' */
iov.iov_len = sizeof(int);
/* Set 'msghdr' fields that describe ancillary data */
msgh.msg_control = controlMsg.buf;
msgh.msg_controllen = sizeof(controlMsg.buf);
/* Receive real plus ancillary data; content of real data is ignored */
nr = recvmsg(sockfd, &msgh, 0);
if (nr == -1)
return -1;
cmsgp = CMSG_FIRSTHDR(&msgh);
/* Check the validity of the 'cmsghdr' */
if (cmsgp == NULL ||
cmsgp->cmsg_len != CMSG_LEN(sizeof(int)) ||
cmsgp->cmsg_level != SOL_SOCKET ||
cmsgp->cmsg_type != SCM_RIGHTS) {
errno = EINVAL;
return -1;
}
/* Return the received file descriptor to our caller */
return *((int *) CMSG_DATA(cmsgp));
}

View File

@ -0,0 +1,26 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU Lesser General Public License as published *
* by the Free Software Foundation, either version 3 or (at your option) *
* any later version. This program is distributed without any warranty. *
* See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_functions.h
Functions to exchange ancillary data over a UNIX domain socket.
*/
#ifndef SCM_FUNCTIONS_H
#define SCM_FUNCTIONS_H /* Prevent accidental double inclusion */
#include "af_unix_hdr.h"
int sendfd(int sockfd, int fd);
int recvfd(int sockfd);
#endif

View File

@ -0,0 +1,94 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU Lesser General Public License as published *
* by the Free Software Foundation, either version 3 or (at your option) *
* any later version. This program is distributed without any warranty. *
* See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Solution for Exercise 59-3:b */
/* unix_sockets.c
A package of useful routines for UNIX domain sockets.
*/
#include "unix_sockets.h" /* Declares functions defined here */
#include "af_unix_hdr.h"
/* Build a UNIX domain socket address structure for 'path', returning
it in 'addr'. Returns -1 on success, or 0 on error. */
int
unixBuildAddress(const char *path, struct sockaddr_un *addr)
{
if (addr == NULL || path == NULL) {
errno = EINVAL;
return -1;
}
memset(addr, 0, sizeof(struct sockaddr_un));
addr->sun_family = AF_UNIX;
if (strlen(path) < sizeof(addr->sun_path)) {
strncpy(addr->sun_path, path, sizeof(addr->sun_path) - 1);
return 0;
} else {
errno = ENAMETOOLONG;
return -1;
}
}
/* Create a UNIX domain socket of type 'type' and connect it
to the remote address specified by the 'path'.
Return the socket descriptor on success, or -1 on error */
int
unixConnect(const char *path, int type)
{
int sd, savedErrno;
struct sockaddr_un addr;
if (unixBuildAddress(path, &addr) == -1)
return -1;
sd = socket(AF_UNIX, type, 0);
if (sd == -1)
return -1;
if (connect(sd, (struct sockaddr *) &addr,
sizeof(struct sockaddr_un)) == -1) {
savedErrno = errno;
close(sd); /* Might change 'errno' */
errno = savedErrno;
return -1;
}
return sd;
}
/* Create a UNIX domain socket and bind it to 'path'.
Return the socket descriptor on success, or -1 on error. */
int
unixBind(const char *path, int type)
{
int sd, savedErrno;
struct sockaddr_un addr;
if (unixBuildAddress(path, &addr) == -1)
return -1;
sd = socket(AF_UNIX, type, 0);
if (sd == -1)
return -1;
if (bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) {
savedErrno = errno;
close(sd); /* Might change 'errno' */
errno = savedErrno;
return -1;
}
return sd;
}

View File

@ -0,0 +1,28 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU Lesser General Public License as published *
* by the Free Software Foundation, either version 3 or (at your option) *
* any later version. This program is distributed without any warranty. *
* See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Solution for Exercise 59-3:a */
/* unix_sockets.h
Header file for unix_sockets.c.
*/
#ifndef UNIX_SOCKETS_H
#define UNIX_SOCKETS_H /* Prevent accidental double inclusion */
#include "af_unix_hdr.h"
int unixBuildAddress(const char *path, struct sockaddr_un *addr);
int unixConnect(const char *path, int type);
int unixBind(const char *path, int type);
#endif

View File

@ -0,0 +1,9 @@
/* Header for msg_peek_sv.c and msg_peek_cl.c */
#include "af_unix_hdr.h"
#define SV_SOCK_PATH "/tmp/peek"
#define BUF_SIZE 100
#define BACKLOG 5

View File

@ -0,0 +1,20 @@
#include "msg_peek.h"
int
main ()
{
int sfd;
ssize_t nread;
char buf[BUF_SIZE];
if ((sfd = unixConnect (SV_SOCK_PATH, SOCK_STREAM)) < 0)
errExit ("unixConnect");
/* Copy stdin to socket. */
while ((nread = read (STDIN_FILENO, buf, BUF_SIZE)) > 0)
if (write (sfd, buf, nread) != nread)
errExit ("partial/failed write");
if (nread < 0)
errExit("read");
}

View File

@ -0,0 +1,36 @@
#include "msg_peek.h"
int
main ()
{
int sfd, cfd;
ssize_t nread;
char buf[BUF_SIZE];
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
errExit ("remove");
if ((sfd = unixBind (SV_SOCK_PATH, SOCK_STREAM)) < 0)
errExit ("unixBind");
if (listen (sfd, BACKLOG) < 0)
errExit ("listen");
cfd = accept (sfd, NULL, NULL);
if (cfd < 0)
errExit ("accept");
printf ("peeking...\n");
if ((nread = recv (cfd, buf, BUF_SIZE - 1, MSG_PEEK)) < 0)
errExit ("recv");
buf[nread] = '\0';
printf ("reading would yield %zd bytes: %s\n", nread, buf);
sleep (1);
printf ("reading...\n");
if ((nread = read (cfd, buf, BUF_SIZE)) < 0)
errExit ("read");
buf[nread] = '\0';
printf ("read %zd bytes: %s\n", nread, buf);
if (close (cfd) < 0)
errExit ("close");
}

View File

@ -0,0 +1,41 @@
/* Adapted from https://www.oreilly.com/library/view/linux-system-programming/0596009585/ch04.html */
#include "scatter_gather.h"
int main ()
{
char foo[48], bar[51], baz[49];
struct iovec iov[3];
ssize_t nr;
int sfd, cfd;
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
errExit ("remove");
if ((sfd = unixBind (SV_SOCK_PATH, SOCK_STREAM)) < 0)
errExit ("unixBind");
if (listen (sfd, BACKLOG) < 0)
errExit ("listen");
cfd = accept (sfd, NULL, NULL);
if (cfd < 0)
errExit ("accept");
iov[0].iov_base = foo;
iov[0].iov_len = sizeof (foo);
iov[1].iov_base = bar;
iov[1].iov_len = sizeof (bar);
iov[2].iov_base = baz;
iov[2].iov_len = sizeof (baz);
nr = readv (cfd, iov, 3);
if (nr < 0)
errExit ("readv");
printf ("read %zd bytes\n", nr);
for (int i = 0; i < 3; i++)
printf ("%d: %s", i, (char *) iov[i].iov_base);
if (close (cfd) < 0)
errExit ("close");
}

View File

@ -0,0 +1,7 @@
/* Header for readv_socket.c and writev_socket.c */
#include "af_unix_hdr.h"
#define SV_SOCK_PATH "/tmp/scatter"
#define BACKLOG 5

View File

@ -0,0 +1,21 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_cred.h
Header file used by scm_cred_send.c and scm_cred_recv.c.
*/
#define _GNU_SOURCE /* To get SCM_CREDENTIALS definition from
<sys/socket.h> */
#include "af_unix_hdr.h"
#define SOCK_PATH "/tmp/scm_cred"

View File

@ -0,0 +1,173 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_cred_recv.c
Used in conjunction with scm_cred_send.c to demonstrate passing of
process credentials via a UNIX domain socket.
This program receives credentials sent to a UNIX domain socket.
Usage is as shown in the usageErr() call below.
Credentials can be exchanged over stream or datagram sockets. This program
uses stream sockets by default; the "-d" command-line option specifies
that datagram sockets should be used instead.
This program is Linux-specific.
See also scm_multi_recv.c.
*/
#include "scm_cred.h"
int
main(int argc, char *argv[])
{
int data, lfd, sfd, optval, opt;
ssize_t nr;
Boolean useDatagramSocket;
struct msghdr msgh;
struct iovec iov;
struct ucred *ucredp, ucred;
/* Allocate a char array of suitable size to hold the ancillary data.
However, since this buffer is in reality a 'struct cmsghdr', use a
union to ensure that it is aligned as required for that structure.
Alternatively, we could allocate the buffer using malloc(), which
returns a buffer that satisfies the strictest alignment
requirements of any type */
union {
char buf[CMSG_SPACE(sizeof(struct ucred))];
/* Space large enough to hold a 'ucred' structure */
struct cmsghdr align;
} controlMsg;
struct cmsghdr *cmsgp; /* Pointer used to iterate through
headers in ancillary data */
socklen_t len;
/* Parse command-line options */
useDatagramSocket = FALSE;
while ((opt = getopt(argc, argv, "d")) != -1) {
switch (opt) {
case 'd':
useDatagramSocket = TRUE;
break;
default:
usageErr("%s [-d]\n"
" -d use datagram socket\n", argv[0]);
}
}
/* Create socket bound to a well-known address. In the case where
we are using stream sockets, also make the socket a listening
socket and accept a connection on the socket. */
if (remove(SOCK_PATH) == -1 && errno != ENOENT)
errExit("remove-%s", SOCK_PATH);
if (useDatagramSocket) {
sfd = unixBind(SOCK_PATH, SOCK_DGRAM);
if (sfd == -1)
errExit("unixBind");
} else {
lfd = unixBind(SOCK_PATH, SOCK_STREAM);
if (lfd == -1)
errExit("unixBind");
if (listen(lfd, 5) == -1)
errExit("listen");
sfd = accept(lfd, NULL, NULL);
if (sfd == -1)
errExit("accept");
}
/* We must set the SO_PASSCRED socket option in order to receive
credentials */
optval = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1)
errExit("setsockopt");
/* The 'msg_name' field can be set to point to a buffer where the
kernel will place the address of the peer socket. However, we don't
need the address of the peer, so we set this field to NULL. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* Set fields of 'msgh' to point to buffer used to receive (real)
data read by recvmsg() */
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
/* Set 'msgh' fields to describe the ancillary data buffer */
msgh.msg_control = controlMsg.buf;
msgh.msg_controllen = sizeof(controlMsg.buf);
/* Receive real plus ancillary data */
nr = recvmsg(sfd, &msgh, 0);
if (nr == -1)
errExit("recvmsg");
printf("recvmsg() returned %ld\n", (long) nr);
if (nr > 0)
printf("Received data = %d\n", data);
/* Get the address of the first 'cmsghdr' in the received
ancillary data */
cmsgp = CMSG_FIRSTHDR(&msgh);
/* Check the validity of the 'cmsghdr' */
if (cmsgp == NULL || cmsgp->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
fatal("bad cmsg header / message length");
if (cmsgp->cmsg_level != SOL_SOCKET)
fatal("cmsg_level != SOL_SOCKET");
if (cmsgp->cmsg_type != SCM_CREDENTIALS)
fatal("cmsg_type != SCM_CREDENTIALS");
/* The data area of the 'cmsghdr' is a 'struct ucred', so assign
the address of the data area to a suitable pointer */
ucredp = (struct ucred *) CMSG_DATA(cmsgp);
/* Display the credentials from the received data area */
printf("Received credentials pid=%ld, uid=%ld, gid=%ld\n",
(long) ucredp->pid, (long) ucredp->uid, (long) ucredp->gid);
/* The Linux-specific, read-only SO_PEERCRED socket option returns
credential information about the peer, as described in socket(7).
This operation can be performed on UNIX domain stream sockets and on
UNIX domain sockets (stream or datagram) created with socketpair(). */
len = sizeof(struct ucred);
if (getsockopt(sfd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
errExit("getsockopt");
printf("Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n",
(long) ucred.pid, (long) ucred.uid, (long) ucred.gid);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,171 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_cred_send.c
Used in conjunction with scm_cred_recv.c to demonstrate passing of
process credentials via a UNIX domain socket.
This program sends credentials to a UNIX domain socket.
Usage is as shown in the usageErr() call below.
Credentials can be exchanged over stream or datagram sockets. This program
uses stream sockets by default; the "-d" command-line option specifies
that datagram sockets should be used instead.
This program is Linux-specific.
See also scm_multi_send.c.
*/
#include "scm_cred.h"
int
main(int argc, char *argv[])
{
int data, sfd, opt;
ssize_t ns;
Boolean useDatagramSocket, noExplicitCreds;
struct msghdr msgh;
struct iovec iov;
/* Allocate a char array of suitable size to hold the ancillary data.
However, since this buffer is in reality a 'struct cmsghdr', use a
union to ensure that it is aligned as required for that structure.
Alternatively, we could allocate the buffer using malloc(), which
returns a buffer that satisfies the strictest alignment
requirements of any type */
union {
char buf[CMSG_SPACE(sizeof(struct ucred))];
/* Space large enough to hold a ucred structure */
struct cmsghdr align;
} controlMsg;
struct cmsghdr *cmsgp; /* Pointer used to iterate through
headers in ancillary data */
/* Parse command-line options */
useDatagramSocket = FALSE;
noExplicitCreds = FALSE;
while ((opt = getopt(argc, argv, "dn")) != -1) {
switch (opt) {
case 'd':
useDatagramSocket = TRUE;
break;
case 'n':
noExplicitCreds = TRUE;
break;
default:
usageErr("%s [-d] [-n] [data [PID [UID [GID]]]]\n"
" -d use datagram socket\n"
" -n don't construct explicit "
"credentials structure\n", argv[0]);
}
}
/* The 'msg_name' field can be used to specify the address of the
destination socket when sending a datagram. However, we do not
need to use this field because we use connect() below, which sets
a default outgoing address for datagrams. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* On Linux, we must transmit at least 1 byte of real data in
order to send ancillary data */
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
/* Data is optionally taken from command line */
data = (argc > optind) ? atoi(argv[optind]) : 12345;
fprintf(stderr, "Sending data = %d\n", data);
if (noExplicitCreds) {
/* Don't construct an explicit credentials structure. (It is not
necessary to do so, if we just want the receiver to receive
our real credentials.) */
printf("Not explicitly sending a credentials structure\n");
msgh.msg_control = NULL;
msgh.msg_controllen = 0;
} else {
struct ucred *ucredp;
/* Set 'msgh' fields to describe the ancillary data buffer */
msgh.msg_control = controlMsg.buf;
msgh.msg_controllen = sizeof(controlMsg.buf);
/* The control message buffer must be zero-initialized in order for the
CMSG_NXTHDR() macro to work correctly. Although we don't need to use
CMSG_NXTHDR() in this example (because there is only one block of
ancillary data), we show this step to demonstrate best practice */
memset(controlMsg.buf, 0, sizeof(controlMsg.buf));
/* Set message header to describe the ancillary data that
we want to send */
cmsgp = CMSG_FIRSTHDR(&msgh);
cmsgp->cmsg_len = CMSG_LEN(sizeof(struct ucred));
cmsgp->cmsg_level = SOL_SOCKET;
cmsgp->cmsg_type = SCM_CREDENTIALS;
/* Set 'ucredp' to point to the data area in the 'cmsghdr' */
ucredp = (struct ucred *) CMSG_DATA(cmsgp);
/* Use sender's own PID, real UID, and real GID, unless
alternate values were supplied on the command line */
ucredp->pid = getpid();
if (argc > optind + 1 && strcmp(argv[optind + 1], "-") != 0)
ucredp->pid = atoi(argv[optind + 1]);
ucredp->uid = getuid();
if (argc > optind + 2 && strcmp(argv[optind + 2], "-") != 0)
ucredp->uid = atoi(argv[optind + 2]);
ucredp->gid = getgid();
if (argc > optind + 3 && strcmp(argv[optind + 3], "-") != 0)
ucredp->gid = atoi(argv[optind + 3]);
printf("Send credentials pid=%ld, uid=%ld, gid=%ld\n",
(long) ucredp->pid, (long) ucredp->uid, (long) ucredp->gid);
}
/* Connect to the peer socket */
sfd = unixConnect(SOCK_PATH, useDatagramSocket ? SOCK_DGRAM : SOCK_STREAM);
if (sfd == -1)
errExit("unixConnect");
/* Send real plus ancillary data */
ns = sendmsg(sfd, &msgh, 0);
if (ns == -1)
errExit("sendmsg");
printf("sendmsg() returned %ld\n", (long) ns);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,26 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_multi.h
Header file used by scm_multi_send.c and scm_multi_recv.c.
*/
#define _GNU_SOURCE /* To get SCM_CREDENTIALS definition from
<sys/socket.h> */
#include <sys/stat.h>
#include <fcntl.h>
#include "unix_sockets.h" /* Declares our socket functions */
#include "af_unix_hdr.h"
#define SOCK_PATH "scm_multi"
#define MAX_FDS 1024 /* Maximum number of file descriptors we'll
attempt to exchange in ancillary data */

View File

@ -0,0 +1,249 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_multi_recv.c
Used in conjunction with scm_multi_send.c to demonstrate passing of
ancillary data containing multiple 'msghdr' structures on a UNIX
domain socket.
Usage is as shown in the usageErr() call below.
This program uses stream sockets by default; the "-d" command-line option
specifies that datagram sockets should be used instead.
This program is Linux-specific.
*/
#define _GNU_SOURCE
#include "scm_multi.h"
#define BUF_SIZE 100
int
main(int argc, char *argv[])
{
int data, lfd, sfd, opt, optval, j;
ssize_t NumReceived;
Boolean useDatagramSocket;
int optControlMsgSize;
struct msghdr msgh;
struct iovec iov;
char *controlMsg; /* Ancillary data (control message) */
size_t controlMsgSize; /* Size of ancillary data */
struct cmsghdr *cmsgp; /* Pointer used to iterate through
headers in ancillary data */
struct ucred *ucredp; /* Pointer to data area of a 'cmsghdr' that
contains credentials */
int *fdList; /* Pointer to data area of a 'cmsghdr' that
contains a list of file descriptors */
int fdCnt; /* Number of FDs in ancillary data */
/* Allocate a buffer of suitable size to hold the ancillary data.
This buffer is in reality treated as a 'struct cmsghdr',
and so needs to be suitably aligned: malloc() provides a block
with suitable alignment. */
controlMsgSize = CMSG_SPACE(sizeof(int[MAX_FDS])) +
CMSG_SPACE(sizeof(struct ucred));
controlMsg = malloc(controlMsgSize);
if (controlMsg == NULL)
errExit("malloc");
/* Parse command-line options */
useDatagramSocket = FALSE;
optControlMsgSize = -1;
while ((opt = getopt(argc, argv, "dn:")) != -1) {
switch (opt) {
case 'd':
useDatagramSocket = TRUE;
break;
case 'n':
optControlMsgSize = atoi(optarg);
break;
default:
usageErr("%s [-d]\n"
" -d use datagram socket\n"
" -n nbytes limit on size of received "
"ancillary data\n", argv[0]);
}
}
/* Create socket bound to a well-known address. In the case where
we are using stream sockets, also make the socket a listening
socket and accept a connection on the socket. */
if (remove(SOCK_PATH) == -1 && errno != ENOENT)
errExit("remove-%s", SOCK_PATH);
if (useDatagramSocket) {
sfd = unixBind(SOCK_PATH, SOCK_DGRAM);
if (sfd == -1)
errExit("unixBind");
} else {
lfd = unixBind(SOCK_PATH, SOCK_STREAM);
if (lfd == -1)
errExit("unixBind");
if (listen(lfd, 5) == -1)
errExit("listen");
sfd = accept(lfd, NULL, NULL);
if (sfd == -1)
errExit("accept");
}
/* We must set the SO_PASSCRED socket option in order to receive
credentials */
optval = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1)
errExit("setsockopt");
/* The 'msg_name' field can be set to point to a buffer where the
kernel will place the address of the peer socket. However, we don't
need the address of the peer, so we set this field to NULL. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* Set fields of 'msgh' to point to a buffer used to receive
the (real) data read by recvmsg() */
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
/* Set 'msgh' fields to describe the ancillary data buffer.
The 'optControlMsgSize' value (specified as a command-line option)
can be used to artifically limit the size of the received ancillary
data. This can be used to demonstrate that when the buffer size is
too small, the list of received file descriptors is truncated, and
the excess file descriptors are automatically closed. */
msgh.msg_control = controlMsg;
msgh.msg_controllen = (optControlMsgSize == -1) ?
controlMsgSize : optControlMsgSize;
/* Receive real plus ancillary data */
NumReceived = recvmsg(sfd, &msgh, 0);
if (NumReceived == -1)
errExit("recvmsg");
printf("recvmsg() returned %ld\n", (long) NumReceived);
if (NumReceived > 0)
printf("Received data = %d\n", data);
if (optControlMsgSize != -1) {
char cbuf[1000];
/* Display this process's set of open file descriptors via
/proc/PID/fd */
printf("=================================\n");
snprintf(cbuf, sizeof(cbuf), "ls -l /proc/%ld/fd", (long) getpid());
system(cbuf);
printf("=================================\n");
}
/* Check to see if the ancillary data was truncated */
if (msgh.msg_flags & MSG_CTRUNC)
printf("********** Ancillary data was truncated!!! **********\n");
/* Walk through the series of headers in the ancillary data */
for (cmsgp = CMSG_FIRSTHDR(&msgh);
cmsgp != NULL;
cmsgp = CMSG_NXTHDR(&msgh, cmsgp)) {
printf("=================================\n");
printf("cmsg_len: %ld\n", (long) cmsgp->cmsg_len);
/* Check that 'cmsg_level' is as expected */
if (cmsgp->cmsg_level != SOL_SOCKET)
fatal("cmsg_level != SOL_SOCKET");
switch (cmsgp->cmsg_type) {
case SCM_RIGHTS: /* Header containing file descriptors */
/* The number of file descriptors is the size of the control
message block minus the size that would be allocated for
a zero-length data block (i.e., the size of the 'cmsghdr'
structure plus padding), divided by the size of a file
descriptor */
fdCnt = (cmsgp->cmsg_len - CMSG_LEN(0)) / sizeof(int);
printf("SCM_RIGHTS: received %d file descriptors\n", fdCnt);
/* Set 'fdList' to point to the first descriptor in the
control message data */
fdList = ((int *) CMSG_DATA(cmsgp));
/* For each of the received file descriptors, display the file
descriptor number and read and display the file content */
for (j = 0; j < fdCnt; j++) {
printf("--- [%d] Received FD %d\n", j, fdList[j]);
for (;;) {
char buf[BUF_SIZE];
ssize_t numRead;
numRead = read(fdList[j], buf, BUF_SIZE);
if (numRead == -1)
errExit("read");
if (numRead == 0)
break;
write(STDOUT_FILENO, buf, numRead);
}
if (close(fdList[j]) == -1)
errExit("close");
}
break;
case SCM_CREDENTIALS: /* Header containing credentials */
/* Check validity of the 'cmsghdr' */
if (cmsgp->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
fatal("cmsg data has incorrect size");
/* The data in this control message block is a 'struct ucred' */
ucredp = (struct ucred *) CMSG_DATA(cmsgp);
printf("SCM_CREDENTIALS: pid=%ld, uid=%ld, gid=%ld\n",
(long) ucredp->pid, (long) ucredp->uid,
(long) ucredp->gid);
break;
default:
fatal("Bad cmsg_type (%d)", cmsgp->cmsg_type);
}
}
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,226 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_multi_send.c
Used in conjunction with scm_multi_recv.c to demonstrate passing of
ancillary data containing multiple 'msghdr' structures on a UNIX
domain socket.
This program sends ancillary data consisting of two blocks. One block
contains process credentials (SCM_CREDENTIALS) and the other contains
one or more file descriptors (SCM_RIGHTS).
Usage is as shown below in usageError().
This program uses stream sockets by default; the "-d" command-line option
specifies that datagram sockets should be used instead.
This program is Linux-specific.
*/
#define _GNU_SOURCE
#include "scm_multi.h"
static void
usageError(char *pname)
{
fprintf(stderr, "Usage: %s [options] file...\n", pname);
fprintf(stderr, " Options:\n");
fprintf(stderr, "\t-d Use datagram (instead of stream) socket\n");
fprintf(stderr, "\t-n Don't send any real data with the "
"ancillary data\n");
fprintf(stderr, "\t-p <pid> Use this PID when sending credentials\n");
fprintf(stderr, "\t-u <uid> Use this UID when sending credentials\n");
fprintf(stderr, "\t-g <gid> Use this GID when sending credentials\n");
fprintf(stderr, " If any of any of -p/-u/-g is absent, the "
"corresponding real\n credential is used.\n");
exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
int data, sfd, opt, j;
pid_t pid;
uid_t uid;
gid_t gid;
ssize_t numSent;
Boolean useDatagramSocket, sendData;
struct msghdr msgh;
struct iovec iov;
struct ucred *ucredp; /* Pointer to data area of a 'cmsghdr' that
contains credentials */
int *fdList; /* Pointer to data area of a 'cmsghdr' that
contains a list of file descriptors */
int fdCnt; /* Number of FDs in ancillary data */
char *controlMsg; /* Ancillary data (control message) */
size_t controlMsgSize; /* Size of ancillary data */
struct cmsghdr *cmsgp; /* Pointer used to iterate through
headers in ancillary data */
/* By default, this program sends an SCM_CREDENTIALS message containing
the process's real credentials. This can be altered via command-line
options. */
pid = getpid();
uid = getuid();
gid = getgid();
/* Parse command-line options */
useDatagramSocket = FALSE;
sendData = TRUE;
while ((opt = getopt(argc, argv, "dnp:u:g:")) != -1) {
switch (opt) {
case 'd':
useDatagramSocket = TRUE;
break;
case 'n':
sendData = FALSE;
break;
case 'p':
pid = atoi(optarg);
break;
case 'u':
uid = atoi(optarg);
break;
case 'g':
gid = atoi(optarg);
break;
default:
usageError(argv[0]);
}
}
fdCnt = argc - optind;
if (fdCnt <= 0)
usageError(argv[0]);
/* Allocate a buffer of suitable size to hold the ancillary data.
This buffer is in reality treated as a 'struct cmsghdr',
and so needs to be suitably aligned: malloc() provides a block
with suitable alignment. */
controlMsgSize = CMSG_SPACE(sizeof(int) * fdCnt) +
CMSG_SPACE(sizeof(struct ucred));
controlMsg = malloc(controlMsgSize);
if (controlMsg == NULL)
errExit("malloc");
/* The control message buffer must be zero-initialized in order for
the CMSG_NXTHDR() macro to work correctly */
memset(controlMsg, 0, controlMsgSize);
/* The 'msg_name' field can be used to specify the address of the
destination socket when sending a datagram. However, we do not
need to use this field because we use connect() below, which sets
a default outgoing address for datagrams. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* On Linux, we must transmit at least 1 byte of real data in
order to send ancillary data, at least when using stream sockets.
The following allows for testing the results if no real data is
sent with the ancillary data. */
if (sendData) {
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
data = 12345;
} else {
msgh.msg_iov = NULL;
msgh.msg_iovlen = 0;
}
/* Place a pointer to the ancillary data, and size of that data,
in the 'msghdr' structure that will be passed to sendmsg() */
msgh.msg_control = controlMsg;
msgh.msg_controllen = controlMsgSize;
/* Set message header to describe the ancillary data that
we want to send */
/* First, the file descriptor list */
cmsgp = CMSG_FIRSTHDR(&msgh);
cmsgp->cmsg_level = SOL_SOCKET;
cmsgp->cmsg_type = SCM_RIGHTS;
/* The ancillary message must include space for the required number
of file descriptors */
cmsgp->cmsg_len = CMSG_LEN(sizeof(int) * fdCnt);
printf("cmsg_len 1: %ld\n", (long) cmsgp->cmsg_len);
/* Set 'fdList' pointing to the data area of this ancillary message block.
The file descriptrs are placed into the data block by the loop below. */
fdList = (int *) CMSG_DATA(cmsgp);
/* Next, the credentials */
cmsgp = CMSG_NXTHDR(&msgh, cmsgp);
cmsgp->cmsg_level = SOL_SOCKET;
cmsgp->cmsg_type = SCM_CREDENTIALS;
/* The ancillary message must include space for a 'struct ucred' */
cmsgp->cmsg_len = CMSG_LEN(sizeof(struct ucred));
printf("cmsg_len 2: %ld\n", (long) cmsgp->cmsg_len);
/* Set 'ucredp' pointing to the data area of this ancillary message block.
The credentials are placed into the data area by code below. */
ucredp = (struct ucred *) CMSG_DATA(cmsgp);
/* Initialize the credentials inside the ancillary data */
ucredp->pid = pid;
ucredp->uid = uid;
ucredp->gid = gid;
/* Open the files named on the command line, placing the returned file
descriptors into the ancillary data */
for (j = 0; j < fdCnt; j++) {
fdList[j] = open(argv[optind + j], O_RDONLY);
if (fdList[j] == -1)
errExit("open");
}
/* Connect to the peer socket */
sfd = unixConnect(SOCK_PATH, useDatagramSocket ? SOCK_DGRAM : SOCK_STREAM);
if (sfd == -1)
errExit("unixConnect");
/* Send the data plus ancillary data */
numSent = sendmsg(sfd, &msgh, 0);
if (numSent == -1)
errExit("sendmsg");
printf("sendmsg() returned %ld\n", (long) numSent);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,21 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_rights.h
Header file used by scm_rights_send.c and scm_rights_recv.c.
*/
#include <fcntl.h>
#include "unix_sockets.h" /* Declares our unix*() socket functions */
#include "af_unix_hdr.h"
#define SOCK_PATH "scm_rights"

View File

@ -0,0 +1,169 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_rights_recv.c
Used in conjunction with scm_rights_send.c to demonstrate passing of
file descriptors via a UNIX domain socket.
This program receives a file descriptor sent to a UNIX domain socket.
Usage is as shown in the usageErr() call below.
File descriptors can be exchanged over stream or datagram sockets. This
program uses stream sockets by default; the "-d" command-line option
specifies that datagram sockets should be used instead.
This program is Linux-specific.
See also scm_multi_recv.c.
*/
#include "scm_rights.h"
#define BUF_SIZE 100
int
main(int argc, char *argv[])
{
int data, lfd, sfd, fd, opt;
ssize_t nr;
Boolean useDatagramSocket;
struct msghdr msgh;
struct iovec iov;
/* Allocate a char array of suitable size to hold the ancillary data.
However, since this buffer is in reality a 'struct cmsghdr', use a
union to ensure that it is aligned as required for that structure.
Alternatively, we could allocate the buffer using malloc(), which
returns a buffer that satisfies the strictest alignment
requirements of any type */
union {
char buf[CMSG_SPACE(sizeof(int))];
/* Space large enough to hold an 'int' */
struct cmsghdr align;
} controlMsg;
struct cmsghdr *cmsgp; /* Pointer used to iterate through
headers in ancillary data */
/* Parse command-line options */
useDatagramSocket = FALSE;
while ((opt = getopt(argc, argv, "d")) != -1) {
switch (opt) {
case 'd':
useDatagramSocket = TRUE;
break;
default:
usageErr("%s [-d]\n"
" -d use datagram socket\n", argv[0]);
}
}
/* Create socket bound to a well-known address. In the case where
we are using stream sockets, also make the socket a listening
socket and accept a connection on the socket. */
if (remove(SOCK_PATH) == -1 && errno != ENOENT)
errExit("remove-%s", SOCK_PATH);
if (useDatagramSocket) {
sfd = unixBind(SOCK_PATH, SOCK_DGRAM);
if (sfd == -1)
errExit("unixBind");
} else {
lfd = unixBind(SOCK_PATH, SOCK_STREAM);
if (lfd == -1)
errExit("unixBind");
if (listen(lfd, 5) == -1)
errExit("listen");
sfd = accept(lfd, NULL, NULL);
if (sfd == -1)
errExit("accept");
}
/* The 'msg_name' field can be set to point to a buffer where the
kernel will place the address of the peer socket. However, we don't
need the address of the peer, so we set this field to NULL. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* Set fields of 'msgh' to point to buffer used to receive the (real)
data read by recvmsg() */
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
/* Set 'msgh' fields to describe the ancillary data buffer */
msgh.msg_control = controlMsg.buf;
msgh.msg_controllen = sizeof(controlMsg.buf);
/* Receive real plus ancillary data */
nr = recvmsg(sfd, &msgh, 0);
if (nr == -1)
errExit("recvmsg");
fprintf(stderr, "recvmsg() returned %ld\n", (long) nr);
if (nr > 0)
fprintf(stderr, "Received data = %d\n", data);
/* Get the address of the first 'cmsghdr' in the received
ancillary data */
cmsgp = CMSG_FIRSTHDR(&msgh);
/* Check the validity of the 'cmsghdr' */
if (cmsgp == NULL || cmsgp->cmsg_len != CMSG_LEN(sizeof(int)))
fatal("bad cmsg header / message length");
if (cmsgp->cmsg_level != SOL_SOCKET)
fatal("cmsg_level != SOL_SOCKET");
if (cmsgp->cmsg_type != SCM_RIGHTS)
fatal("cmsg_type != SCM_RIGHTS");
/* The data area of the 'cmsghdr' is an 'int', so assign
the address of the data area to a suitable pointer. The data
is the received file descriptor (which is typically a different
file descriptor number than was used in the sending process). */
fd = *((int *) CMSG_DATA(cmsgp));
fprintf(stderr, "Received FD %d\n", fd);
/* Having obtained the file descriptor, read the file's contents and
print them on standard output */
for (;;) {
char buf[BUF_SIZE];
ssize_t numRead;
numRead = read(fd, buf, BUF_SIZE);
if (numRead == -1)
errExit("read");
if (numRead == 0)
break;
write(STDOUT_FILENO, buf, numRead);
}
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,138 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_rights_send.c
Used in conjunction with scm_rights_recv.c to demonstrate passing of
file descriptors via a UNIX domain socket.
This program sends a file descriptor to a UNIX domain socket.
Usage is as shown in the usageErr() call below.
File descriptors can be exchanged over stream or datagram sockets. This
program uses stream sockets by default; the "-d" command-line option
specifies that datagram sockets should be used instead.
This program is Linux-specific.
See also scm_multi_recv.c.
*/
#include "scm_rights.h"
int
main(int argc, char *argv[])
{
int data, sfd, opt, fd;
ssize_t ns;
Boolean useDatagramSocket;
struct msghdr msgh;
struct iovec iov;
/* Allocate a char array of suitable size to hold the ancillary data.
However, since this buffer is in reality a 'struct cmsghdr', use a
union to ensure that it is aligned as required for that structure.
Alternatively, we could allocate the buffer using malloc(), which
returns a buffer that satisfies the strictest alignment
requirements of any type. */
union {
char buf[CMSG_SPACE(sizeof(int))];
/* Space large enough to hold an 'int' */
struct cmsghdr align;
} controlMsg;
struct cmsghdr *cmsgp; /* Pointer used to iterate through
headers in ancillary data */
/* Parse command-line options */
useDatagramSocket = FALSE;
while ((opt = getopt(argc, argv, "d")) != -1) {
switch (opt) {
case 'd':
useDatagramSocket = TRUE;
break;
default:
usageErr("%s [-d] file\n"
" -d use datagram socket\n", argv[0]);
}
}
if (argc != optind + 1)
usageErr("%s [-d] file\n", argv[0]);
/* Open the file named on the command line */
fd = open(argv[optind], O_RDONLY);
if (fd == -1)
errExit("open");
/* The 'msg_name' field can be used to specify the address of the
destination socket when sending a datagram. However, we do not
need to use this field because we use connect() below, which sets
a default outgoing address for datagrams. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* On Linux, we must transmit at least 1 byte of real data in
order to send ancillary data */
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
data = 12345;
fprintf(stderr, "Sending data = %d\n", data);
/* Set 'msgh' fields to describe the ancillary data buffer */
msgh.msg_control = controlMsg.buf;
msgh.msg_controllen = sizeof(controlMsg.buf);
/* The control message buffer must be zero-initialized in order
for the CMSG_NXTHDR() macro to work correctly. Although we
don't need to use CMSG_NXTHDR() in this example (because
there is only one block of ancillary data), we show this
step to demonstrate best practice */
memset(controlMsg.buf, 0, sizeof(controlMsg.buf));
/* Set message header to describe the ancillary data that
we want to send */
cmsgp = CMSG_FIRSTHDR(&msgh);
cmsgp->cmsg_len = CMSG_LEN(sizeof(int));
cmsgp->cmsg_level = SOL_SOCKET;
cmsgp->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA(cmsgp)) = fd;
/* Connect to the peer socket */
sfd = unixConnect(SOCK_PATH, useDatagramSocket ? SOCK_DGRAM : SOCK_STREAM);
if (sfd == -1)
errExit("unixConnect");
fprintf(stderr, "Sending FD %d\n", fd);
/* Send real plus ancillary data */
ns = sendmsg(sfd, &msgh, 0);
if (ns == -1)
errExit("sendmsg");
fprintf(stderr, "sendmsg() returned %ld\n", (long) ns);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,54 @@
#include "select_test.h"
int
main ()
{
int sfd, flags;
fd_set writefds;
size_t nwritten = 0;
ssize_t nw;
char buf[BUF_SIZE];
if ((sfd = unixConnect (SV_SOCK_PATH, SOCK_STREAM)) < 0)
errExit ("unixConnect");
flags = fcntl (sfd, F_GETFL);
if (fcntl (sfd, F_SETFL, flags | O_NONBLOCK) < 0)
errExit ("fcntl");
printf ("waiting for socket to be ready for write...\n");
FD_ZERO (&writefds);
FD_SET (sfd, &writefds);
if (select (sfd + 1, NULL, &writefds, NULL, NULL) < 0)
errExit ("select");
if (FD_ISSET (sfd, &writefds))
printf ("ready for write, writing until buffer full\n");
else
errExit ("something's wrong");
while (1)
{
nw = write (sfd, buf, BUF_SIZE);
if (nw < 0)
{
if (errno == EAGAIN)
{
printf ("buffer full\n");
break;
}
else
errExit ("write");
}
nwritten += nw;
}
printf ("wrote %zu bytes\n", nwritten);
printf ("waiting for write ready again...\n");
FD_ZERO (&writefds);
FD_SET (sfd, &writefds);
if (select (sfd + 1, NULL, &writefds, NULL, NULL) < 0)
errExit ("select");
if (FD_ISSET (sfd, &writefds))
printf ("ready for write, writing once more\n");
if ((nw = write (sfd, buf, BUF_SIZE)) < 0)
errExit ("write");
nwritten += nw;
printf ("wrote %zd more bytes for a total of %zu\n", nw, nwritten);
}

View File

@ -0,0 +1,59 @@
#include "select_test.h"
int
main ()
{
int sfd, cfd, flags;
fd_set readfds;
size_t nread = 0;
char buf[BUF_SIZE];
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
errExit ("remove");
if ((sfd = unixBind (SV_SOCK_PATH, SOCK_STREAM)) < 0)
errExit ("unixBind");
if (listen (sfd, BACKLOG) < 0)
errExit ("listen");
printf ("waiting for connection request...\n");
FD_ZERO (&readfds);
FD_SET (sfd, &readfds);
if (select (sfd + 1, &readfds, NULL, NULL, NULL) < 0)
errExit ("select");
if (FD_ISSET (sfd, &readfds))
printf ("connection request received; accepting\n");
else
errExit ("something's wrong");
cfd = accept (sfd, NULL, NULL);
if (cfd < 0)
errExit ("accept");
flags = fcntl (cfd, F_GETFL);
if (fcntl (cfd, F_SETFL, flags | O_NONBLOCK) < 0)
errExit ("fcntl");
printf ("slowly reading from socket...\n");
while (1)
{
FD_ZERO (&readfds);
FD_SET (cfd, &readfds);
if (select (cfd + 1, &readfds, NULL, NULL, NULL) < 0)
errExit ("select");
if (!FD_ISSET (cfd, &readfds))
errExit ("something's wrong");
ssize_t nr = read (cfd, buf, 10);
if (nr < 0)
{
if (errno == EPIPE)
break;
else
errExit ("read");
}
else if (nr == 0)
break;
nread += nr;
}
printf ("read %zu bytes\n", nread);
}

View File

@ -0,0 +1,11 @@
/* Header for select_sv.c and select_cl.c */
#include "af_unix_hdr.h"
#include <sys/select.h>
#include <fcntl.h>
#define SV_SOCK_PATH "/tmp/select"
#define BUF_SIZE 65527 /* MAX_AF_PKT_LEN - sizeof (af_unix_pkt_hdr_t) */
#define BACKLOG 5

View File

@ -0,0 +1,32 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 57-5 */
/* ud_ucase.h
Header file for ud_ucase_sv.c and ud_ucase_cl.c.
These programs employ sockets in /tmp. This makes it easy to compile
and run the programs. However, for a security reasons, a real-world
application should never create sensitive files in /tmp. (As a simple of
example of the kind of security problems that can result, a malicious
user could create a file using the name defined in SV_SOCK_PATH, and
thereby cause a denial of service attack against this application.
See Section 38.7 of "The Linux Programming Interface" for more details
on this subject.)
*/
#include <ctype.h>
#include "af_unix_hdr.h"
#define BUF_SIZE 10 /* Maximum size of messages exchanged
between client and server */
#define SV_SOCK_PATH "/tmp/ud_ucase"

View File

@ -0,0 +1,71 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 57-7 */
/* ud_ucase_cl.c
A UNIX domain client that communicates with the server in ud_ucase_sv.c.
This client sends each command-line argument as a datagram to the server,
and then displays the contents of the server's response datagram.
*/
#include "ud_ucase.h"
int
main(int argc, char *argv[])
{
struct sockaddr_un svaddr, claddr;
int sfd, j;
size_t msgLen;
ssize_t numBytes;
char resp[BUF_SIZE];
if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s msg...\n", argv[0]);
/* Create client socket; bind to unique pathname (based on PID) */
sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sfd == -1)
errExit("socket");
memset(&claddr, 0, sizeof(struct sockaddr_un));
claddr.sun_family = AF_UNIX;
snprintf(claddr.sun_path, sizeof(claddr.sun_path),
"/tmp/ud_ucase_cl.%ld", (long) getpid());
if (bind(sfd, (struct sockaddr *) &claddr, sizeof(struct sockaddr_un)) == -1)
errExit("bind");
/* Construct address of server */
memset(&svaddr, 0, sizeof(struct sockaddr_un));
svaddr.sun_family = AF_UNIX;
strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);
/* Send messages to server; echo responses on stdout */
for (j = 1; j < argc; j++) {
msgLen = strlen(argv[j]); /* May be longer than BUF_SIZE */
if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_un)) != msgLen)
fatal("sendto");
numBytes = recvfrom(sfd, resp, BUF_SIZE, 0, NULL, NULL);
/* Or equivalently: numBytes = recv(sfd, resp, BUF_SIZE, 0);
or: numBytes = read(sfd, resp, BUF_SIZE); */
if (numBytes == -1)
errExit("recvfrom");
printf("Response %d: %.*s\n", j, (int) numBytes, resp);
}
remove(claddr.sun_path); /* Remove client socket pathname */
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,72 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 57-6 */
/* ud_ucase_sv.c
A server that uses a UNIX domain datagram socket to receive datagrams,
convert their contents to uppercase, and then return them to the senders.
See also ud_ucase_cl.c.
*/
#include "ud_ucase.h"
int
main(int argc, char *argv[])
{
struct sockaddr_un svaddr, claddr;
int sfd, j;
ssize_t numBytes;
socklen_t len;
char buf[BUF_SIZE];
sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Create server socket */
if (sfd == -1)
errExit("socket");
/* Construct well-known address and bind server socket to it */
/* For an explanation of the following check, see the erratum note for
page 1168 at http://www.man7.org/tlpi/errata/. */
if (strlen(SV_SOCK_PATH) > sizeof(svaddr.sun_path) - 1)
fatal("Server socket path too long: %s", SV_SOCK_PATH);
if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
errExit("remove-%s", SV_SOCK_PATH);
memset(&svaddr, 0, sizeof(struct sockaddr_un));
svaddr.sun_family = AF_UNIX;
strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);
if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1)
errExit("bind");
/* Receive messages, convert to uppercase, and return to client */
for (;;) {
len = sizeof(struct sockaddr_un);
numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
(struct sockaddr *) &claddr, &len);
if (numBytes == -1)
errExit("recvfrom");
printf("Server received %ld bytes from %s\n", (long) numBytes,
claddr.sun_path);
for (j = 0; j < numBytes; j++)
buf[j] = toupper((unsigned char) buf[j]);
if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len) !=
numBytes)
fatal("sendto");
}
}

View File

@ -0,0 +1,61 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 57-8 */
/* us_abstract_bind.c
Demonstrate how to bind a UNIX domain socket to a name in the
Linux-specific abstract namespace.
This program is Linux-specific.
The first printing of the book used slightly different code. The code was
correct, but could have been better (to understand why, see the errata
for page 1176 of the book). The old code is shown in comments below.
*/
#include "af_unix_hdr.h"
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_un addr;
char *str;
memset(&addr, 0, sizeof(struct sockaddr_un)); /* Clear address structure */
addr.sun_family = AF_UNIX; /* UNIX domain address */
/* addr.sun_path[0] has already been set to 0 by memset() */
str = "xyz"; /* Abstract name is "\0xyz" */
strncpy(&addr.sun_path[1], str, strlen(str));
// In early printings of the book, the above two lines were instead:
//
// strncpy(&addr.sun_path[1], "xyz", sizeof(addr.sun_path) - 2);
// /* Abstract name is "xyz" followed by null bytes */
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1)
errExit("socket");
if (bind(sockfd, (struct sockaddr *) &addr,
sizeof(sa_family_t) + strlen(str) + 1) == -1)
errExit("bind");
// In early printings of the book, the final part of the bind() call
// above was instead:
// sizeof(struct sockaddr_un)) == -1)
sleep(60);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,30 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 57-2 */
/* us_xfr.h
Header file for us_xfr_sv.c and us_xfr_cl.c.
These programs employ a socket in /tmp. This makes it easy to compile
and run the programs. However, for a security reasons, a real-world
application should never create sensitive files in /tmp. (As a simple of
example of the kind of security problems that can result, a malicious
user could create a file using the name defined in SV_SOCK_PATH, and
thereby cause a denial of service attack against this application.
See Section 38.7 of "The Linux Programming Interface" for more details
on this subject.)
*/
#include "af_unix_hdr.h"
#define SV_SOCK_PATH "/tmp/us_xfr"
#define BUF_SIZE 100

View File

@ -0,0 +1,55 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 57-4 */
/* us_xfr_cl.c
An example UNIX domain stream socket client. This client transmits contents
of stdin to a server socket.
See also us_xfr_sv.c.
*/
#include "us_xfr.h"
int
main(int argc, char *argv[])
{
struct sockaddr_un addr;
int sfd;
ssize_t numRead;
char buf[BUF_SIZE];
sfd = socket(AF_UNIX, SOCK_STREAM, 0); /* Create client socket */
if (sfd == -1)
errExit("socket");
/* Construct server address, and make the connection */
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);
if (connect(sfd, (struct sockaddr *) &addr,
sizeof(struct sockaddr_un)) == -1)
errExit("connect");
/* Copy stdin to socket */
while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE)) > 0)
if (write(sfd, buf, numRead) != numRead)
fatal("partial/failed write");
if (numRead == -1)
errExit("read");
exit(EXIT_SUCCESS); /* Closes our socket; server sees EOF */
}

View File

@ -0,0 +1,79 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Listing 57-3 */
/* us_xfr_sv.c
An example UNIX stream socket server. Accepts incoming connections
and copies data sent from clients to stdout.
See also us_xfr_cl.c.
*/
#include "us_xfr.h"
#define BACKLOG 5
int
main(int argc, char *argv[])
{
struct sockaddr_un addr;
int sfd, cfd;
ssize_t numRead;
char buf[BUF_SIZE];
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sfd == -1)
errExit("socket");
/* Construct server socket address, bind socket to it,
and make this a listening socket */
/* For an explanation of the following check, see the errata notes for
pages 1168 and 1172 at http://www.man7.org/tlpi/errata/. */
if (strlen(SV_SOCK_PATH) > sizeof(addr.sun_path) - 1)
fatal("Server socket path too long: %s", SV_SOCK_PATH);
if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
errExit("remove-%s", SV_SOCK_PATH);
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);
if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1)
errExit("bind");
if (listen(sfd, BACKLOG) == -1)
errExit("listen");
for (;;) { /* Handle client connections iteratively */
/* Accept a connection. The connection is returned on a new
socket, 'cfd'; the listening socket ('sfd') remains open
and can be used to accept further connections. */
cfd = accept(sfd, NULL, NULL);
if (cfd == -1)
errExit("accept");
/* Transfer data from connected socket to stdout until EOF */
while ((numRead = read(cfd, buf, BUF_SIZE)) > 0)
if (write(STDOUT_FILENO, buf, numRead) != numRead)
fatal("partial/failed write");
if (numRead == -1)
errExit("read");
if (close(cfd) == -1)
errMsg("close");
}
}

View File

@ -0,0 +1,22 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Solution for Exercise 59-3:c */
/* us_xfr_v2.h
Header file for us_xfr_sv.c and us_xfr_cl.c.
*/
#include "unix_sockets.h" /* Declares our socket functions */
#include "af_unix_hdr.h"
#define SV_SOCK_PATH "/tmp/us_xfr_v2"
#define BUF_SIZE 100

View File

@ -0,0 +1,44 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Solution for Exercise 59-3:e */
/* us_xfr_v2_cl.c
An example UNIX domain stream socket client. This client transmits contents
of stdin to a server socket. This program is similar to us_xfr_cl.c, except
that it uses the functions in unix_sockets.c to simplify working with UNIX
domain sockets.
See also us_xfr_v2_sv.c.
*/
#include "us_xfr_v2.h"
int
main(int argc, char *argv[])
{
int sfd;
ssize_t numRead;
char buf[BUF_SIZE];
sfd = unixConnect(SV_SOCK_PATH, SOCK_STREAM);
if (sfd == -1)
errExit("unixConnect");
/* Copy stdin to socket */
while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE)) > 0)
if (write(sfd, buf, numRead) != numRead)
fatal("partial/failed write");
if (numRead == -1)
errExit("read");
exit(EXIT_SUCCESS); /* Closes our socket; server sees EOF */
}

View File

@ -0,0 +1,57 @@
/*************************************************************************\
* Copyright (C) Michael Kerrisk, 2018. *
* *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 or (at your option) any *
* later version. This program is distributed without any warranty. See *
* the file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Solution for Exercise 59-3:d */
/* us_xfr_v2_sv.c
An example UNIX stream socket server. Accepts incoming connections and
copies data sent from clients to stdout. This program is similar to
us_xfr_sv.c, except that it uses the functions in unix_sockets.c to
simplify working with UNIX domain sockets.
See also us_xfr_v2_cl.c.
*/
#include "us_xfr_v2.h"
int
main(int argc, char *argv[])
{
int sfd, cfd;
ssize_t numRead;
char buf[BUF_SIZE];
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
errExit ("remove");
sfd = unixBind(SV_SOCK_PATH, SOCK_STREAM);
if (sfd == -1)
errExit("unixBind");
if (listen(sfd, 5) == -1)
errExit("listen");
for (;;) { /* Handle client connections iteratively */
cfd = accept(sfd, NULL, NULL);
if (cfd == -1)
errExit("accept");
/* Transfer data from connected socket to stdout until EOF */
while ((numRead = read(cfd, buf, BUF_SIZE)) > 0)
if (write(STDOUT_FILENO, buf, numRead) != numRead)
fatal("partial/failed write");
if (numRead == -1)
errExit("read");
if (close(cfd) == -1)
errMsg("close");
}
}

View File

@ -0,0 +1,7 @@
/* Header for waitall_sv.c and waitall_cl.c */
#include "af_unix_hdr.h"
#define SV_SOCK_PATH "/tmp/waitall"
#define BACKLOG 5

View File

@ -0,0 +1,22 @@
#include "waitall.h"
#define BUF_SIZE 100
int
main ()
{
int sfd;
ssize_t nread;
char buf[BUF_SIZE];
if ((sfd = unixConnect (SV_SOCK_PATH, SOCK_STREAM)) < 0)
errExit ("unixConnect");
/* Copy stdin to socket. */
while ((nread = read (STDIN_FILENO, buf, BUF_SIZE)) > 0)
if (write (sfd, buf, nread) != nread)
errExit ("partial/failed write");
if (nread < 0)
errExit ("read");
}

View File

@ -0,0 +1,38 @@
#include "waitall.h"
#define BUF_SIZE 10
int
main ()
{
int sfd, cfd;
ssize_t nread;
char buf[BUF_SIZE];
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
errExit ("remove");
if ((sfd = unixBind (SV_SOCK_PATH, SOCK_STREAM)) < 0)
errExit ("unixBind");
if (listen (sfd, BACKLOG) < 0)
errExit ("listen");
while (1)
{
cfd = accept (sfd, NULL, NULL);
if (cfd < 0)
errExit ("accept");
/* Transfer data from connected socket to stdout until EOF. */
while ((nread = recv (cfd, buf, BUF_SIZE, MSG_WAITALL)) > 0)
if (write (STDOUT_FILENO, buf, nread) != nread)
errExit ("partial/failed write");
if (nread < 0)
errExit ("read");
if (close (cfd) < 0)
errExit ("close");
}
}

View File

@ -0,0 +1,32 @@
/* Adapted from https://www.oreilly.com/library/view/linux-system-programming/0596009585/ch04.html */
#include "scatter_gather.h"
int main ( )
{
struct iovec iov[3];
ssize_t nr;
int sfd;
char *buf[] = {
"The term buccaneer comes from the word boucan.\n",
"A boucan is a wooden frame used for cooking meat.\n",
"Buccaneer is the West Indies name for a pirate.\n"
};
if ((sfd = unixConnect (SV_SOCK_PATH, SOCK_STREAM)) < 0)
errExit ("unixConnect");
for (int i = 0; i < 3; i++)
{
iov[i].iov_base = buf[i];
iov[i].iov_len = strlen (buf[i]) + 1;
}
nr = writev (sfd, iov, 3);
if (nr < 0)
errExit ("writev");
printf ("wrote %zd bytes\n", nr);
if (close (sfd) < 0)
errExit ("close");
}