-
Notifications
You must be signed in to change notification settings - Fork 0
/
ssdp2wol.cpp
142 lines (125 loc) · 5.32 KB
/
ssdp2wol.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright (c) 2009 - Mozy, Inc.
#include "mordor/predef.h"
#include <iostream>
#include "mordor/config.h"
#include "mordor/daemon.h"
#include "mordor/iomanager.h"
#include "mordor/main.h"
#include "mordor/socket.h"
#include "mordor/string.h"
#include "mordor/http/parser.h"
using namespace Mordor;
static ConfigVar<std::string>::ptr g_macAddress = Config::lookup("macaddress",
std::string(), "MAC address to send WOL to");
static ConfigVar<std::string>::ptr g_interface = Config::lookup("interface",
std::string("eth0"), "Interface to listen and send on");
static ConfigVar<std::string>::ptr g_blacklist = Config::lookup("blacklist",
std::string(), "Semicolon separate list of IP addresses to ignore");
static void wol(Socket::ptr socket, const std::string &macAddress) {
MORDOR_ASSERT(macAddress.size() == 6u);
std::string message;
message.append(6u, (char)0xff);
for(size_t i = 0; i < 16; ++i)
message.append(macAddress);
socket->send(message.c_str(), message.size());
}
static int daemonMain(int argc, char *argv[])
{
try {
std::string macAddressString = g_macAddress->val();
replace(macAddressString, "-", "");
if (macAddressString.size() != 12u) {
std::cerr << "MAC address must be 12 characters" << std::endl;
return -1;
}
std::string macAddress = dataFromHexstring(macAddressString);
std::set<Address::ptr> blacklistedAddresses;
std::vector<std::string> blacklistedAddressesString = split(
g_blacklist->val(), ";, ");
for(std::vector<std::string>::const_iterator it(
blacklistedAddressesString.begin());
it != blacklistedAddressesString.end();
++it) {
if(it->empty())
continue;
blacklistedAddresses.insert(IPAddress::create(it->c_str()));
}
std::vector<std::pair<Address::ptr, unsigned int> > addresses =
Address::getInterfaceAddresses(g_interface->val(), AF_INET);
if (addresses.empty()) {
std::cerr << "Couldn't find interface " << g_interface->val()
<< std::endl;
return -1;
}
IPAddress::ptr localAddress = boost::static_pointer_cast<IPAddress>(
addresses.front().first);
IPAddress::ptr broadcastAddress = localAddress->broadcastAddress(
addresses.front().second);
broadcastAddress->port(9u);
IPv4Address multicastAddress("239.255.255.250", 1900);
IOManager ioManager;
Socket::ptr broadcastSocket(broadcastAddress->createSocket(ioManager,
SOCK_DGRAM));
broadcastSocket->setOption(SOL_SOCKET, SO_BROADCAST, 1);
broadcastSocket->connect(broadcastAddress);
Socket::ptr listenSocket(multicastAddress.createSocket(ioManager,
SOCK_DGRAM));
listenSocket->setOption(SOL_SOCKET, SO_REUSEADDR, 1);
listenSocket->bind(IPv4Address(0u, 1900u));
// TODO: listenSocket->joinGroup(multicastAddress, addresses.front().first);
struct ip_mreq multicastGroup;
memcpy(&multicastGroup.imr_multiaddr, &((sockaddr_in *)multicastAddress.name())->sin_addr, sizeof(struct in_addr));
memcpy(&multicastGroup.imr_interface, &((sockaddr_in *)addresses.front().first->name())->sin_addr, sizeof(struct in_addr));
listenSocket->setOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, multicastGroup);
Daemon::onTerminate.connect(boost::bind(&Socket::cancelReceive,
listenSocket));
try {
IPv4Address sender;
char buffer[4096];
size_t size;
while((size = listenSocket->receiveFrom(buffer, 4096, sender))) {
IPAddress::ptr senderDuplicate = sender.clone();
senderDuplicate->port(0u);
if (blacklistedAddresses.find(senderDuplicate) !=
blacklistedAddresses.end()) {
MORDOR_LOG_VERBOSE(Log::root())
<< "Skipping broadcast from " << sender;
continue;
}
HTTP::Request request;
HTTP::RequestParser parser(request);
parser.run(buffer, size);
if (parser.complete() && !parser.error()) {
if (request.requestLine.method == "M-SEARCH") {
MORDOR_LOG_INFO(Log::root()) << "Relaying M-SEARCH to WOL from "
<< sender;
wol(broadcastSocket, macAddress);
}
} else {
MORDOR_LOG_WARNING(Log::root()) << "Unable to parse HTTP request from "
<< sender << ": " << charslice(buffer, size);
}
}
} catch (OperationAbortedException &) {
} catch (...) {
MORDOR_LOG_FATAL(Log::root())
<< boost::current_exception_diagnostic_information();
return -1;
}
} catch (...) {
std::cerr << boost::current_exception_diagnostic_information() << std::endl;
return -1;
}
return 0;
}
MORDOR_MAIN(int argc, char *argv[])
{
try {
Config::loadFromEnvironment();
return Daemon::run(argc, argv, &daemonMain);
} catch (...) {
std::cerr << boost::current_exception_diagnostic_information()
<< std::endl;
return -1;
}
}