cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

wol.c (6520B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "config.h"
     21
     22#include "guacamole/error.h"
     23#include "guacamole/wol.h"
     24
     25#include <stdio.h>
     26#include <stdlib.h>
     27#include <string.h>
     28#include <unistd.h>
     29#include <arpa/inet.h>
     30#include <netinet/in.h>
     31#include <sys/socket.h>
     32
     33/**
     34 * Generate the magic Wake-on-LAN (WoL) packet for the specified MAC address
     35 * and place it in the character array.
     36 * 
     37 * @param packet
     38 *     The character array that will contain the generated packet.
     39 * 
     40 * @param mac_address
     41 *     The unsigned int representation of the MAC address to place in the packet.
     42 */
     43static void __guac_wol_create_magic_packet(unsigned char packet[],
     44        unsigned int mac_address[]) {
     45    
     46    int i;
     47    unsigned char mac[6];
     48    
     49    /* Concurrently fill the first part of the packet with 0xFF, and copy the
     50     MAC address from the int array to the char array. */
     51    for (i = 0; i < 6; i++) {
     52        packet[i] = 0xFF;
     53        mac[i] = mac_address[i];
     54    }
     55    
     56    /* Copy the MAC address contents into the char array that is storing
     57     the rest of the packet. */
     58    for (i = 1; i <= 16; i++) {
     59        memcpy(&packet[i * 6], &mac, 6 * sizeof(unsigned char));
     60    }
     61    
     62}
     63
     64/**
     65 * Send the magic Wake-on-LAN (WoL) packet to the specified broadcast address,
     66 * returning the number of bytes sent, or zero if any error occurred and nothing
     67 * was sent.
     68 * 
     69 * @param broadcast_addr
     70 *     The broadcast address to which to send the magic WoL packet.
     71 * 
     72 * @param udp_port
     73 *     The UDP port to use when sending the WoL packet.
     74 * 
     75 * @param packet
     76 *     The magic WoL packet to send.
     77 * 
     78 * @return 
     79 *     The number of bytes sent, or zero if nothing could be sent.
     80 */
     81static ssize_t __guac_wol_send_packet(const char* broadcast_addr,
     82        const unsigned short udp_port, unsigned char packet[]) {
     83    
     84    struct sockaddr_in wol_dest;
     85    int wol_socket;
     86    
     87    /* Determine the IP version, starting with IPv4. */
     88    wol_dest.sin_port = htons(udp_port);
     89    wol_dest.sin_family = AF_INET;
     90    int retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr));
     91    
     92    /* If return value is less than zero, this system doesn't know about IPv4. */
     93    if (retval < 0) {
     94        guac_error = GUAC_STATUS_SEE_ERRNO;
     95        guac_error_message = "IPv4 address family is not supported";
     96        return 0;
     97    }
     98    
     99    /* If return value is zero, address doesn't match the IPv4, so try IPv6. */
    100    else if (retval == 0) {
    101        wol_dest.sin_family = AF_INET6;
    102        retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr));
    103        
    104        /* System does not support IPv6. */
    105        if (retval < 0) {
    106            guac_error = GUAC_STATUS_SEE_ERRNO;
    107            guac_error_message = "IPv6 address family is not supported";
    108            return 0;
    109        }
    110        
    111        /* Address didn't match IPv6. */
    112        else if (retval == 0) {
    113            guac_error = GUAC_STATUS_INVALID_ARGUMENT;
    114            guac_error_message = "Invalid broadcast or multicast address specified for Wake-on-LAN";
    115            return 0;
    116        }
    117    }
    118    
    119    
    120    
    121    /* Set up the socket */
    122    wol_socket = socket(wol_dest.sin_family, SOCK_DGRAM, 0);
    123    
    124    /* If socket open fails, bail out. */
    125    if (wol_socket < 0) {
    126        guac_error = GUAC_STATUS_SEE_ERRNO;
    127        guac_error_message = "Failed to open socket to send Wake-on-LAN packet";
    128        return 0;
    129    }
    130    
    131    /* Set up socket for IPv4 broadcast. */
    132    if (wol_dest.sin_family == AF_INET) {
    133        
    134        /* For configuring socket broadcast */
    135        int wol_bcast = 1;
    136
    137        /* Attempt to set IPv4 broadcast; exit with error if this fails. */
    138        if (setsockopt(wol_socket, SOL_SOCKET, SO_BROADCAST, &wol_bcast,
    139                sizeof(wol_bcast)) < 0) {
    140            close(wol_socket);
    141            guac_error = GUAC_STATUS_SEE_ERRNO;
    142            guac_error_message = "Failed to set IPv4 broadcast for Wake-on-LAN socket";
    143            return 0;
    144        }
    145    }
    146    
    147    /* Set up socket for IPv6 multicast. */
    148    else {
    149        
    150        /* Stick to a single hop for now. */
    151        int hops = 1;
    152        
    153        /* Attempt to set IPv6 multicast; exit with error if this fails. */
    154        if (setsockopt(wol_socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
    155                sizeof(hops)) < 0) {
    156            close(wol_socket);
    157            guac_error = GUAC_STATUS_SEE_ERRNO;
    158            guac_error_message = "Failed to set IPv6 multicast for Wake-on-LAN socket";
    159            return 0;
    160        }
    161    }
    162    
    163    /* Send the packet and return number of bytes sent. */
    164    int bytes = sendto(wol_socket, packet, GUAC_WOL_PACKET_SIZE, 0,
    165            (struct sockaddr*) &wol_dest, sizeof(wol_dest));
    166    close(wol_socket);
    167    return bytes;
    168 
    169}
    170
    171int guac_wol_wake(const char* mac_addr, const char* broadcast_addr,
    172        const unsigned short udp_port) {
    173    
    174    unsigned char wol_packet[GUAC_WOL_PACKET_SIZE];
    175    unsigned int dest_mac[6];
    176    
    177    /* Parse mac address and return with error if parsing fails. */
    178    if (sscanf(mac_addr, "%x:%x:%x:%x:%x:%x",
    179            &(dest_mac[0]), &(dest_mac[1]), &(dest_mac[2]),
    180            &(dest_mac[3]), &(dest_mac[4]), &(dest_mac[5])) != 6) {
    181        guac_error = GUAC_STATUS_INVALID_ARGUMENT;
    182        guac_error_message = "Invalid argument for Wake-on-LAN MAC address";
    183        return -1;
    184    }
    185    
    186    /* Generate the magic packet. */
    187    __guac_wol_create_magic_packet(wol_packet, dest_mac);
    188    
    189    /* Send the packet and record bytes sent. */
    190    int bytes_sent = __guac_wol_send_packet(broadcast_addr, udp_port, 
    191            wol_packet);
    192    
    193    /* Return 0 if bytes were sent, otherwise return an error. */
    194    if (bytes_sent)
    195        return 0;
    196    
    197    return -1;
    198}