From 88e1bb514c7df15beaf471e21185e7446960aa02 Mon Sep 17 00:00:00 2001 From: KusaReMKN <48670724+KusaReMKN@users.noreply.github.com> Date: Mon, 28 Mar 2022 19:12:35 +0900 Subject: [PATCH] Renew All woltest was renewed as sendwol. --- .gitignore | 52 +++++++ LICENSE | 38 ++--- Makefile.am | 4 + README.md | 60 ++++++-- configure | 12 -- configure.ac | 28 ++++ sendwol.1 | 88 ++++++++++++ sendwol.c | 339 +++++++++++++++++++++++++++++++++++++++++++++ src/Makefile.gcc | 18 --- src/Makefile.pmake | 18 --- src/main.c | 65 --------- 11 files changed, 582 insertions(+), 140 deletions(-) create mode 100644 Makefile.am delete mode 100755 configure create mode 100644 configure.ac create mode 100644 sendwol.1 create mode 100644 sendwol.c delete mode 100644 src/Makefile.gcc delete mode 100644 src/Makefile.pmake delete mode 100644 src/main.c diff --git a/.gitignore b/.gitignore index c6127b3..3350f26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +## C # Prerequisites *.d @@ -50,3 +51,54 @@ modules.order Module.symvers Mkfile.old dkms.conf + +## AUTOTOOLS +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap + +# http://www.gnu.org/software/autoconf + +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ + +/ltmain.sh + +# http://www.gnu.org/software/texinfo + +/texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 + +# Generated Makefile +# (meta build system like autotools, +# can automatically generate from config.status script +# (which is called by configure script)) +Makefile diff --git a/LICENSE b/LICENSE index ac57424..3aacee8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,25 @@ -MIT License +BSD 2-Clause License -Copyright (c) 2021 KusaReMKN +Copyright (c) 2022, KusaReMKN +All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..5b62f0a --- /dev/null +++ b/Makefile.am @@ -0,0 +1,4 @@ +bin_PROGRAMS = sendwol +sendwol_SOURCES = sendwol.c +man_MANS = sendwol.1 +EXTRA_DIST = LICENSE README.md $(man_MANS) diff --git a/README.md b/README.md index 246101c..f2ebb9d 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,65 @@ -# woltest -Wake on LAN experiment +# sendwol – a simple Wake-on-LAN client -See also: https://zenn.dev/kusaremkn/articles/1c057acc19e1db +## Usage -## How to build +For example, most simply: ```console -$ ./configure -$ make +% sendwol 00:00:5e:00:53:44 ``` +Multicast to all link-local nodes on IPv4: -## How to use +```console +% sendwol -4 -a 224.0.0.1 -i eth0 00-00-5e-00-53-11 +``` + +More details, refer the man page. ```console -$ woltest macaddr +% man ./sendwol.1 # instantly +% man sendwol # after installation ``` -You should specify the target MAC address as _macaddr_. + +## Installation + +1. Download the tarball from [releases page][]. + +2. Extract them. + ```console + % tar xvf sendwol-*.**.tar.gz + ``` + +3. Change the working directory. + ```console + % cd sendwol-*/. + ``` + +4. Generate `Makefile`. + ```console + % ./configure + ``` + +5. Build the binary. + ```console + % make + ``` + +6. Install them. + ```console + % make install + ``` + +## Uninstallation + +```console +% make uninstall +``` ## License -MIT License. +The 2-Clause BSD License + +[releases page]: https://github.com/KusaReMKN/sendwol/releases/latest diff --git a/configure b/configure deleted file mode 100755 index 7f6cb36..0000000 --- a/configure +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -cd $(dirname $(readlink -f $0)) - -case `uname` in -FreeBSD) - cp src/Makefile.pmake Makefile - ;; -Linux|*) - cp src/Makefile.gcc Makefile - ;; -esac diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..7cc1e9b --- /dev/null +++ b/configure.ac @@ -0,0 +1,28 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.69]) +AC_INIT([sendwol], [0.01], [https://github.com/KusaReMKN/sendwol/issue]) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_SRCDIR([sendwol.c]) +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. + +# Checks for header files. +AC_CHECK_HEADERS([arpa/inet.h locale.h netdb.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/socket.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_RESTRICT +AC_TYPE_SSIZE_T +AC_TYPE_UINT16_T +AC_TYPE_UINT8_T + +# Checks for library functions. +AC_CHECK_FUNCS([memset setlocale socket strtol]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/sendwol.1 b/sendwol.1 new file mode 100644 index 0000000..51c86bc --- /dev/null +++ b/sendwol.1 @@ -0,0 +1,88 @@ +.\" +.\" Copyright (c) 2022 KusaReMKN +.\" Available under the 2-Clause BSD License +.\" +.Dd 28th March 2022 +.Dt SENDWOL 1 +.Os +.Sh NAME +.Nm sendwol +.Nd send Wake-on-LAN magic packets +.Sh SYNOPSIS +.Nm +.Op Fl 4 | Fl 6 +.Op Fl b | Fl a Ar address | Fl h Ar hostname +.Op Fl i Ar interface +.Op Fl p Ar port | Fl s Ar service +.Ar target ... +.Sh DESCRIPTION +The +.Nm +utility wakes up the +.Ar target +by sending Wake-on-LAN magic packets over UDP. +Obviously, the +.Ar target +needs to support Wake-on-LAN technology. +.Pp +The following options are available: +.Bl -tag +.It Fl 4 +Force to use IPv4 only. +.It Fl 6 +Force to use IPv6 only. +.It Fl b +Use the broadcast address of the interface as destination. +This is the default. +.It Fl a Ar address , Fl h Ar hostname +Specify the destination address or host name. +.It Fl i Ar interface +Specify the name of the interface from which packets are sent. +This option applies only if the UDP destination is a multicast address +.Pq IPv4/IPv6 , +or link-local/site-local unicast address +.Pq IPv6 . +.It Fl p Ar port , Fl s Ar service +Specify the destination port number or service name. +The default value is +.Ql 9 . +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +The following will broadcast Wake-on-LAN magic packets to wake up targets +.Ql 00:00:5e:00:53:34 +and +.Ql 00:00:5e:00:53:59 . +.Bd -literal -offset indent +% sendwol '00:00:5e:00:53:34' '00:00:5e:00:53:59' +.Ed +.Pp +The following will send the magic packets to all nodes +on the network link attached to the interface +.Ql eth0 . +The address +.Ql ff02::1 +is named the link-local all-nodes multicast address, +and the packets would reach every node on the network link. +.Bd -literal -offset indent +% sendwol -6 -a ff02::1 -i eth0 00-00-5e-00-53-15 +.Ed +.Sh SEE ALSO +.Xr getaddrinfo 3 , +.Xr getifaddrs 3 , +.Xr inet 4 , +.Xr inet6 4 , +.Xr ip 4 , +.Xr ip6 4 , +.Xr udp 4 , +.Xr ifconfig 8 +.Sh COPYRIGHT +Copyright \(co 2022 +.An KusaReMKN Aq Lk https://github.com/KusaReMKN . +.br +This document and software are available under +.Sy the 2-Clause BSD License . +.Sh BUGS +To report bugs, please create an issue. +.Aq Lk https://github.com/kusaremkn/sendwol/issue diff --git a/sendwol.c b/sendwol.c new file mode 100644 index 0000000..282d2f6 --- /dev/null +++ b/sendwol.c @@ -0,0 +1,339 @@ +/*- + * SPDX short identifier: BSD-2-Clause + * + * Copyright 2022 KusaReMKN + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 48-bit MAC address + */ +struct macaddr { +#define MACADDR_LEN 6 + uint8_t macaddr[MACADDR_LEN]; +} __attribute__((__packed__)); + +/* + * Wake-on-LAN magic packet without password field + */ +struct magic_packet { + struct macaddr mp_sync; +#define MP_TARGET_REP 16 + struct macaddr mp_target[MP_TARGET_REP]; +} __attribute__((__packed__)); + + +static int brd_connect(int domain, const char *restrict servname, + const char *restrict interface); +static struct magic_packet *build_magicpacket(const struct macaddr *target); +static struct macaddr *mac_aton(const char *a); +static const char *multicast_if(int fd, int domain, const char *interface); +static int udp_connect(int domain, const char *restrict nodename, + const char *restrict servname, const char *restrict interface); +static void usage(void); + +int +main(int argc, char *argv[]) +{ + int c, fd, yes; + int broadcast, domain, forcev4, forcev6; + char *interface, *nodename, *servname; + struct macaddr *target; + struct magic_packet *packet; + ssize_t written; + + (void)setlocale(LC_ALL, ""); + + forcev4 = 0; + forcev6 = 0; + broadcast = 0; + interface = NULL; + nodename = NULL; + servname = "9"; + while ((c = getopt(argc, argv, "46ba:h:i:p:s:")) != -1) + switch (c) { + case '4': + forcev4 = 1; + break; + case '6': + forcev6 = 1; + break; + case 'b': + broadcast = 1; + break; + case 'a': + case 'h': + nodename = optarg; + break; + case 'i': + interface = optarg; + break; + case 'p': + case 's': + servname = optarg; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (forcev4 && forcev6) + errx(1, "-4 and -6 cannot be used simultaneously"); + domain = forcev4 ? AF_INET : forcev6 ? AF_INET6 : AF_UNSPEC; + + if (broadcast && nodename != NULL) + errx(1, "-b and -a (or -h) cannot be used simultaneously"); + if (nodename == NULL) + broadcast = 1; + + fd = broadcast + ? brd_connect(domain, servname, interface) + : udp_connect(domain, nodename, servname, interface); + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)) == -1) + err(1, "SO_BROADCAST"); + + for (; *argv != NULL; argv++) { + target = mac_aton(*argv); + if (target == NULL) { + warnx("%s: invalid MAC address", *argv); + continue; + } + packet = build_magicpacket(target); + written = write(fd, packet, sizeof(*packet)); + if (written != sizeof(*packet)) + err(1, "write"); + } + + if (close(fd) == -1) + err(1, "close"); + + return 0; +} + +static int +brd_connect(int domain, const char *restrict servname, + const char *restrict interface) +{ + int fd, port; + char *cause, *endptr; + struct servent *sp; + struct ifaddrs *ifa, *res; + socklen_t sslen; + struct sockaddr_storage ss; + + if (domain != AF_UNSPEC && domain != AF_INET && domain != AF_INET6) + errno = EAFNOSUPPORT, err(1, "brd_connect"); + + port = htons((uint16_t)strtol(servname, &endptr, 10)); + if (*endptr != '\0') { + sp = getservbyname(servname, "udp"); + if (sp == NULL) + errx(1, "unknown service: %s", servname); + port = sp->s_port; + } + + if (getifaddrs(&res) == -1) + err(1, "getifaddrs"); + errno = 0; + for (ifa = res; ifa != NULL; ifa = ifa->ifa_next) { + if (interface != NULL + && strcmp(interface, ifa->ifa_name) != 0 + || ifa->ifa_broadaddr == NULL + || ifa->ifa_broadaddr->sa_family != AF_INET + && ifa->ifa_broadaddr->sa_family != AF_INET6 + || domain != AF_UNSPEC + && ifa->ifa_broadaddr->sa_family != domain) + continue; + + fd = socket(ifa->ifa_broadaddr->sa_family, SOCK_DGRAM, 0); + if (fd == -1) { + cause = "socket"; + continue; + } + + sslen = ifa->ifa_broadaddr->sa_family == AF_INET + ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6); + memcpy(&ss, ifa->ifa_broadaddr, sslen); + if (ss.ss_family == AF_INET) + ((struct sockaddr_in *)&ss)->sin_port = port; + else + ((struct sockaddr_in6 *)&ss)->sin6_port = port; + + if (connect(fd, (struct sockaddr *)&ss, sslen) == -1) { + if (close(fd) == -1) + err(1, "close"); + cause = "connect"; + continue; + } + break; /* SUCCESS */ + } + if (ifa == NULL && errno != 0) + err(1, "%s", cause); + if (ifa == NULL) + errno = ENXIO, err(1, "%s", interface); + freeifaddrs(res); + + return fd; +} + +static struct magic_packet * +build_magicpacket(const struct macaddr *target) +{ + static struct magic_packet mp; + int i; + + memset(&mp.mp_sync, 0xFF, sizeof(mp.mp_sync)); + for (i = 0; i < MP_TARGET_REP; i++) + memcpy(mp.mp_target + i, target, sizeof(mp.mp_target[0])); + + return ∓ +} + +static struct macaddr * +mac_aton(const char *a) +{ + static struct macaddr addr; + int i; + unsigned int tmp[MACADDR_LEN]; + + i = sscanf(a, "%x%*[-:]%x%*[-:]%x%*[-:]%x%*[-:]%x%*[-:]%x", + tmp + 0, tmp + 1, tmp + 2, tmp + 3, tmp + 4, tmp + 5); + if (i != 6) + return NULL; + + for (i = 0; i < MACADDR_LEN; i++) + addr.macaddr[i] = (uint8_t)tmp[i]; + + return &addr; +} + +static const char * +multicast_if(int fd, int domain, const char *interface) +{ + unsigned int ifindex; + struct ifreq ifr; + struct in_addr addr; + + if (domain != AF_INET && domain != AF_INET6) + return errno = EAFNOSUPPORT, "multicast_if"; + + if (domain == AF_INET) { + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) + return interface; + addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + &addr, sizeof(addr)) == -1) + return "IP_MULTICAST_IF"; + } else { + ifindex = if_nametoindex(interface); + if (ifindex == 0) + return interface; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &ifindex, sizeof(ifindex)) == -1) + return "IPV6_MULTICAST_IF"; + } + + return NULL; +} + +static int +udp_connect(int domain, const char *restrict nodename, + const char *restrict servname, const char *restrict interface) +{ + int error, fd; + const char *cause; + struct addrinfo *ai, *res, hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = domain; + hints.ai_socktype = SOCK_DGRAM; + error = getaddrinfo(nodename, servname, &hints, &res); + if (error != 0) + errx(1, "getaddrinfo: %s", gai_strerror(error)); + + for (ai = res; ai != NULL; ai = ai->ai_next) { + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd == -1) { + cause = "socket"; + continue; + } + + if (interface != NULL) { + cause = multicast_if(fd, ai->ai_family, interface); + if (cause != NULL) + continue; + } + + if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) { + if (close(fd) == -1) + err(1, "close"); + cause = "connect"; + continue; + } + break; /* SUCCESS */ + } + if (ai == NULL) + err(1, "%s", cause); + freeaddrinfo(res); + + return fd; +} + + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: sendwol [-4 | -6]" + " [-b | -a address | -h hostname] [-i interface]\n" + " [-p port | -s service] target ...\n"); + exit(1); +} diff --git a/src/Makefile.gcc b/src/Makefile.gcc deleted file mode 100644 index 3ca48be..0000000 --- a/src/Makefile.gcc +++ /dev/null @@ -1,18 +0,0 @@ -PROGRAM = woltest -OBJS = main.o -VPATH = src - -CC = cc -CCFLAG = -Wall - -RM = rm -f - -.PHONY: all -all: $(PROGRAM) - -$(PROGRAM): $(OBJS) - $(CC) -o $@ $^ - -.PHONY: clean -clean: - $(RM) $(PROGRAM) $(OBJS) diff --git a/src/Makefile.pmake b/src/Makefile.pmake deleted file mode 100644 index 6059be9..0000000 --- a/src/Makefile.pmake +++ /dev/null @@ -1,18 +0,0 @@ -PROGRAM = woltest -OBJS = main.o -VPATH = src - -CC = cc -CCFLAG = -Wall - -RM = rm -f - -.PHONY: all -all: $(PROGRAM) - -$(PROGRAM): $(OBJS) - $(CC) -o $(.TARGET) $(.ALLSRC) - -.PHONY: clean -clean: - $(RM) $(PROGRAM) $(OBJS) diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 92c627c..0000000 --- a/src/main.c +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PORT 9 - -int main(int argc, char *argv[]) -{ - int i, err, yes; - char *ptr; - char macaddr[6], magic[102]; - int sock; - struct sockaddr_in addr; - - if (argc != 2) { - fprintf(stderr, "Usage: %s macaddr\n", argv[0]); - exit(EXIT_FAILURE); - } - - /* Building a Magic Packet */ - ptr = argv[1]; - for (i = 0; i < 6; i++) { - macaddr[i] = (char)strtoul(ptr, &ptr, 16); - ptr++; - } - for (i = 0; i < 6; i++) - magic[i] = 0xff; - for (i = 6; i < 102; i++) - magic[i] = macaddr[i % 6]; - - /* Preparing for packet dispatch */ - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { - perror("socket"); - exit(EXIT_FAILURE); - } - yes = 1; - err = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)); - if (err == -1) { - perror("setsockopt"); - exit(EXIT_FAILURE); - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; /* IPv4 */ - addr.sin_addr.s_addr = INADDR_BROADCAST; /* Broadcast */ - addr.sin_port = htons(PORT); /* discard service */ - - /* dispatch packet */ - err = sendto(sock, magic, sizeof(magic), 0, - (const struct sockaddr *)&addr, sizeof(addr)); - if (err == -1) { - perror("sendto"); - exit(EXIT_FAILURE); - } - - close(sock); - - return 0; -}