/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "config.h" #include "guacamole/error.h" #include "guacamole/wol.h" #include #include #include #include #include #include #include /** * Generate the magic Wake-on-LAN (WoL) packet for the specified MAC address * and place it in the character array. * * @param packet * The character array that will contain the generated packet. * * @param mac_address * The unsigned int representation of the MAC address to place in the packet. */ static void __guac_wol_create_magic_packet(unsigned char packet[], unsigned int mac_address[]) { int i; unsigned char mac[6]; /* Concurrently fill the first part of the packet with 0xFF, and copy the MAC address from the int array to the char array. */ for (i = 0; i < 6; i++) { packet[i] = 0xFF; mac[i] = mac_address[i]; } /* Copy the MAC address contents into the char array that is storing the rest of the packet. */ for (i = 1; i <= 16; i++) { memcpy(&packet[i * 6], &mac, 6 * sizeof(unsigned char)); } } /** * Send the magic Wake-on-LAN (WoL) packet to the specified broadcast address, * returning the number of bytes sent, or zero if any error occurred and nothing * was sent. * * @param broadcast_addr * The broadcast address to which to send the magic WoL packet. * * @param packet * The magic WoL packet to send. * * @return * The number of bytes sent, or zero if nothing could be sent. */ static ssize_t __guac_wol_send_packet(const char* broadcast_addr, unsigned char packet[]) { struct sockaddr_in wol_dest; int wol_socket; /* Determine the IP version, starting with IPv4. */ wol_dest.sin_port = htons(GUAC_WOL_PORT); wol_dest.sin_family = AF_INET; int retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr)); /* If return value is less than zero, this system doesn't know about IPv4. */ if (retval < 0) { guac_error = GUAC_STATUS_SEE_ERRNO; guac_error_message = "IPv4 address family is not supported"; return 0; } /* If return value is zero, address doesn't match the IPv4, so try IPv6. */ else if (retval == 0) { wol_dest.sin_family = AF_INET6; retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr)); /* System does not support IPv6. */ if (retval < 0) { guac_error = GUAC_STATUS_SEE_ERRNO; guac_error_message = "IPv6 address family is not supported"; return 0; } /* Address didn't match IPv6. */ else if (retval == 0) { guac_error = GUAC_STATUS_INVALID_ARGUMENT; guac_error_message = "Invalid broadcast or multicast address specified for Wake-on-LAN"; return 0; } } /* Set up the socket */ wol_socket = socket(wol_dest.sin_family, SOCK_DGRAM, 0); /* If socket open fails, bail out. */ if (wol_socket < 0) { guac_error = GUAC_STATUS_SEE_ERRNO; guac_error_message = "Failed to open socket to send Wake-on-LAN packet"; return 0; } /* Set up socket for IPv4 broadcast. */ if (wol_dest.sin_family == AF_INET) { /* For configuring socket broadcast */ int wol_bcast = 1; /* Attempt to set IPv4 broadcast; exit with error if this fails. */ if (setsockopt(wol_socket, SOL_SOCKET, SO_BROADCAST, &wol_bcast, sizeof(wol_bcast)) < 0) { close(wol_socket); guac_error = GUAC_STATUS_SEE_ERRNO; guac_error_message = "Failed to set IPv4 broadcast for Wake-on-LAN socket"; return 0; } } /* Set up socket for IPv6 multicast. */ else { /* Stick to a single hop for now. */ int hops = 1; /* Attempt to set IPv6 multicast; exit with error if this fails. */ if (setsockopt(wol_socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)) < 0) { close(wol_socket); guac_error = GUAC_STATUS_SEE_ERRNO; guac_error_message = "Failed to set IPv6 multicast for Wake-on-LAN socket"; return 0; } } /* Send the packet and return number of bytes sent. */ int bytes = sendto(wol_socket, packet, GUAC_WOL_PACKET_SIZE, 0, (struct sockaddr*) &wol_dest, sizeof(wol_dest)); close(wol_socket); return bytes; } int guac_wol_wake(const char* mac_addr, const char* broadcast_addr) { unsigned char wol_packet[GUAC_WOL_PACKET_SIZE]; unsigned int dest_mac[6]; /* Parse mac address and return with error if parsing fails. */ if (sscanf(mac_addr, "%x:%x:%x:%x:%x:%x", &(dest_mac[0]), &(dest_mac[1]), &(dest_mac[2]), &(dest_mac[3]), &(dest_mac[4]), &(dest_mac[5])) != 6) { guac_error = GUAC_STATUS_INVALID_ARGUMENT; guac_error_message = "Invalid argument for Wake-on-LAN MAC address"; return -1; } /* Generate the magic packet. */ __guac_wol_create_magic_packet(wol_packet, dest_mac); /* Send the packet and record bytes sent. */ int bytes_sent = __guac_wol_send_packet(broadcast_addr, wol_packet); /* Return 0 if bytes were sent, otherwise return an error. */ if (bytes_sent) return 0; return -1; }