198 lines
6.4 KiB
C
198 lines
6.4 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
|
|
/**
|
|
* 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 udp_port
|
|
* The UDP port to use when sending the 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,
|
|
const unsigned short udp_port, unsigned char packet[]) {
|
|
|
|
struct sockaddr_in wol_dest;
|
|
int wol_socket;
|
|
|
|
/* Determine the IP version, starting with IPv4. */
|
|
wol_dest.sin_port = htons(udp_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,
|
|
const unsigned short udp_port) {
|
|
|
|
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, udp_port,
|
|
wol_packet);
|
|
|
|
/* Return 0 if bytes were sent, otherwise return an error. */
|
|
if (bytes_sent)
|
|
return 0;
|
|
|
|
return -1;
|
|
} |