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:
parent
7d9bb0b2bc
commit
a4f00ed42f
4
.gitignore
vendored
4
.gitignore
vendored
@ -7,6 +7,7 @@
|
||||
.#*
|
||||
*#
|
||||
|
||||
*.exe
|
||||
*.flt
|
||||
*.gmo
|
||||
*.info
|
||||
@ -42,3 +43,6 @@ core
|
||||
!core/
|
||||
|
||||
lost+found
|
||||
|
||||
ename.c.inc
|
||||
libafunix.a
|
||||
|
52
winsup/cygwin/socket_tests/Makefile
Normal file
52
winsup/cygwin/socket_tests/Makefile
Normal 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}
|
11
winsup/cygwin/socket_tests/Makefile.inc
Normal file
11
winsup/cygwin/socket_tests/Makefile.inc
Normal 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
|
159
winsup/cygwin/socket_tests/README.txt
Normal file
159
winsup/cygwin/socket_tests/README.txt
Normal 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
|
72
winsup/cygwin/socket_tests/fork_socketpair.c
Normal file
72
winsup/cygwin/socket_tests/fork_socketpair.c
Normal 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");
|
||||
}
|
||||
}
|
53
winsup/cygwin/socket_tests/lib/Build_ename.sh
Normal file
53
winsup/cygwin/socket_tests/lib/Build_ename.sh
Normal 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;
|
||||
}
|
||||
'
|
||||
|
25
winsup/cygwin/socket_tests/lib/Makefile
Normal file
25
winsup/cygwin/socket_tests/lib/Makefile
Normal 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}
|
50
winsup/cygwin/socket_tests/lib/af_unix_hdr.h
Normal file
50
winsup/cygwin/socket_tests/lib/af_unix_hdr.h
Normal 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
|
201
winsup/cygwin/socket_tests/lib/error_functions.c
Normal file
201
winsup/cygwin/socket_tests/lib/error_functions.c
Normal 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);
|
||||
}
|
47
winsup/cygwin/socket_tests/lib/error_functions.h
Normal file
47
winsup/cygwin/socket_tests/lib/error_functions.h
Normal 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
|
146
winsup/cygwin/socket_tests/lib/scm_functions.c
Normal file
146
winsup/cygwin/socket_tests/lib/scm_functions.c
Normal 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));
|
||||
}
|
26
winsup/cygwin/socket_tests/lib/scm_functions.h
Normal file
26
winsup/cygwin/socket_tests/lib/scm_functions.h
Normal 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
|
94
winsup/cygwin/socket_tests/lib/unix_sockets.c
Normal file
94
winsup/cygwin/socket_tests/lib/unix_sockets.c
Normal 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;
|
||||
}
|
28
winsup/cygwin/socket_tests/lib/unix_sockets.h
Normal file
28
winsup/cygwin/socket_tests/lib/unix_sockets.h
Normal 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
|
9
winsup/cygwin/socket_tests/msg_peek.h
Normal file
9
winsup/cygwin/socket_tests/msg_peek.h
Normal 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
|
20
winsup/cygwin/socket_tests/msg_peek_cl.c
Normal file
20
winsup/cygwin/socket_tests/msg_peek_cl.c
Normal 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");
|
||||
}
|
36
winsup/cygwin/socket_tests/msg_peek_sv.c
Normal file
36
winsup/cygwin/socket_tests/msg_peek_sv.c
Normal 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");
|
||||
}
|
41
winsup/cygwin/socket_tests/readv_socket.c
Normal file
41
winsup/cygwin/socket_tests/readv_socket.c
Normal 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");
|
||||
}
|
7
winsup/cygwin/socket_tests/scatter_gather.h
Normal file
7
winsup/cygwin/socket_tests/scatter_gather.h
Normal 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
|
21
winsup/cygwin/socket_tests/scm_cred.h
Normal file
21
winsup/cygwin/socket_tests/scm_cred.h
Normal 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"
|
173
winsup/cygwin/socket_tests/scm_cred_recv.c
Normal file
173
winsup/cygwin/socket_tests/scm_cred_recv.c
Normal 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);
|
||||
}
|
171
winsup/cygwin/socket_tests/scm_cred_send.c
Normal file
171
winsup/cygwin/socket_tests/scm_cred_send.c
Normal 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);
|
||||
}
|
26
winsup/cygwin/socket_tests/scm_multi.h
Normal file
26
winsup/cygwin/socket_tests/scm_multi.h
Normal 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 */
|
249
winsup/cygwin/socket_tests/scm_multi_recv.c
Normal file
249
winsup/cygwin/socket_tests/scm_multi_recv.c
Normal 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);
|
||||
}
|
226
winsup/cygwin/socket_tests/scm_multi_send.c
Normal file
226
winsup/cygwin/socket_tests/scm_multi_send.c
Normal 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);
|
||||
}
|
21
winsup/cygwin/socket_tests/scm_rights.h
Normal file
21
winsup/cygwin/socket_tests/scm_rights.h
Normal 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"
|
169
winsup/cygwin/socket_tests/scm_rights_recv.c
Normal file
169
winsup/cygwin/socket_tests/scm_rights_recv.c
Normal 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);
|
||||
}
|
138
winsup/cygwin/socket_tests/scm_rights_send.c
Normal file
138
winsup/cygwin/socket_tests/scm_rights_send.c
Normal 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);
|
||||
}
|
54
winsup/cygwin/socket_tests/select_cl.c
Normal file
54
winsup/cygwin/socket_tests/select_cl.c
Normal 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);
|
||||
}
|
59
winsup/cygwin/socket_tests/select_sv.c
Normal file
59
winsup/cygwin/socket_tests/select_sv.c
Normal 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);
|
||||
}
|
11
winsup/cygwin/socket_tests/select_test.h
Normal file
11
winsup/cygwin/socket_tests/select_test.h
Normal 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
|
32
winsup/cygwin/socket_tests/ud_ucase.h
Normal file
32
winsup/cygwin/socket_tests/ud_ucase.h
Normal 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"
|
71
winsup/cygwin/socket_tests/ud_ucase_cl.c
Normal file
71
winsup/cygwin/socket_tests/ud_ucase_cl.c
Normal 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);
|
||||
}
|
72
winsup/cygwin/socket_tests/ud_ucase_sv.c
Normal file
72
winsup/cygwin/socket_tests/ud_ucase_sv.c
Normal 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");
|
||||
}
|
||||
}
|
61
winsup/cygwin/socket_tests/us_abstract_bind.c
Normal file
61
winsup/cygwin/socket_tests/us_abstract_bind.c
Normal 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);
|
||||
}
|
30
winsup/cygwin/socket_tests/us_xfr.h
Normal file
30
winsup/cygwin/socket_tests/us_xfr.h
Normal 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
|
55
winsup/cygwin/socket_tests/us_xfr_cl.c
Normal file
55
winsup/cygwin/socket_tests/us_xfr_cl.c
Normal 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 */
|
||||
}
|
79
winsup/cygwin/socket_tests/us_xfr_sv.c
Normal file
79
winsup/cygwin/socket_tests/us_xfr_sv.c
Normal 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");
|
||||
}
|
||||
}
|
22
winsup/cygwin/socket_tests/us_xfr_v2.h
Normal file
22
winsup/cygwin/socket_tests/us_xfr_v2.h
Normal 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
|
44
winsup/cygwin/socket_tests/us_xfr_v2_cl.c
Normal file
44
winsup/cygwin/socket_tests/us_xfr_v2_cl.c
Normal 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 */
|
||||
}
|
57
winsup/cygwin/socket_tests/us_xfr_v2_sv.c
Normal file
57
winsup/cygwin/socket_tests/us_xfr_v2_sv.c
Normal 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");
|
||||
}
|
||||
}
|
7
winsup/cygwin/socket_tests/waitall.h
Normal file
7
winsup/cygwin/socket_tests/waitall.h
Normal 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
|
22
winsup/cygwin/socket_tests/waitall_cl.c
Normal file
22
winsup/cygwin/socket_tests/waitall_cl.c
Normal 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");
|
||||
}
|
38
winsup/cygwin/socket_tests/waitall_sv.c
Normal file
38
winsup/cygwin/socket_tests/waitall_sv.c
Normal 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");
|
||||
}
|
||||
}
|
32
winsup/cygwin/socket_tests/writev_socket.c
Normal file
32
winsup/cygwin/socket_tests/writev_socket.c
Normal 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");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user