1 /*
2 * Hans - IP over ICMP
3 * Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "worker.h"
21 #include "tun.h"
22 #include "exception.h"
23 #include "config.h"
24
25 #include <string.h>
26 #include <syslog.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <sys/select.h>
30 #include <grp.h>
31
32 using namespace std;
33
Magic(const char * magic)34 Worker::TunnelHeader::Magic::Magic(const char *magic)
35 {
36 memset(data, 0, sizeof(data));
37 strncpy(data, magic, sizeof(data));
38 }
39
operator ==(const Magic & other) const40 bool Worker::TunnelHeader::Magic::operator==(const Magic &other) const
41 {
42 return memcmp(data, other.data, sizeof(data)) == 0;
43 }
44
operator !=(const Magic & other) const45 bool Worker::TunnelHeader::Magic::operator!=(const Magic &other) const
46 {
47 return memcmp(data, other.data, sizeof(data)) != 0;
48 }
49
Worker(int tunnelMtu,const char * deviceName,bool answerEcho,uid_t uid,gid_t gid)50 Worker::Worker(int tunnelMtu, const char *deviceName, bool answerEcho, uid_t uid, gid_t gid)
51 {
52 this->tunnelMtu = tunnelMtu;
53 this->answerEcho = answerEcho;
54 this->uid = uid;
55 this->gid = gid;
56 this->privilegesDropped = false;
57
58 echo = NULL;
59 tun = NULL;
60
61 try
62 {
63 echo = new Echo(tunnelMtu + sizeof(TunnelHeader));
64 tun = new Tun(deviceName, tunnelMtu);
65 }
66 catch (...)
67 {
68 delete echo;
69 delete tun;
70
71 throw;
72 }
73 }
74
~Worker()75 Worker::~Worker()
76 {
77 delete echo;
78 delete tun;
79 }
80
sendEcho(const TunnelHeader::Magic & magic,int type,int length,uint32_t realIp,bool reply,uint16_t id,uint16_t seq)81 void Worker::sendEcho(const TunnelHeader::Magic &magic, int type, int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
82 {
83 if (length > payloadBufferSize())
84 throw Exception("packet too big");
85
86 TunnelHeader *header = (TunnelHeader *)echo->sendPayloadBuffer();
87 header->magic = magic;
88 header->type = type;
89
90 DEBUG_ONLY(printf("sending: type %d, length %d, id %d, seq %d\n", type, length, id, seq));
91
92 echo->send(length + sizeof(TunnelHeader), realIp, reply, id, seq);
93 }
94
sendToTun(int length)95 void Worker::sendToTun(int length)
96 {
97 tun->write(echoReceivePayloadBuffer(), length);
98 }
99
setTimeout(Time delta)100 void Worker::setTimeout(Time delta)
101 {
102 nextTimeout = now + delta;
103 }
104
run()105 void Worker::run()
106 {
107 now = Time::now();
108 alive = true;
109
110 int maxFd = echo->getFd() > tun->getFd() ? echo->getFd() : tun->getFd();
111
112 while (alive)
113 {
114 fd_set fs;
115 Time timeout;
116
117 FD_ZERO(&fs);
118 FD_SET(tun->getFd(), &fs);
119 FD_SET(echo->getFd(), &fs);
120
121 if (nextTimeout != Time::ZERO)
122 {
123 timeout = nextTimeout - now;
124 if (timeout < Time::ZERO)
125 timeout = Time::ZERO;
126 }
127
128 // wait for data or timeout
129 int result = select(maxFd + 1 , &fs, NULL, NULL, nextTimeout != Time::ZERO ? &timeout.getTimeval() : NULL);
130 if (result == -1)
131 {
132 if (alive)
133 throw Exception("select", true);
134 else
135 return;
136 }
137 now = Time::now();
138
139 // timeout
140 if (result == 0)
141 {
142 nextTimeout = Time::ZERO;
143 handleTimeout();
144 continue;
145 }
146
147 // icmp data
148 if (FD_ISSET(echo->getFd(), &fs))
149 {
150 bool reply;
151 uint16_t id, seq;
152 uint32_t ip;
153
154 int dataLength = echo->receive(ip, reply, id, seq);
155 if (dataLength != -1)
156 {
157 bool valid = dataLength >= sizeof(TunnelHeader);
158
159 if (valid)
160 {
161 TunnelHeader *header = (TunnelHeader *)echo->receivePayloadBuffer();
162
163 DEBUG_ONLY(printf("received: type %d, length %d, id %d, seq %d\n", header->type, dataLength - sizeof(TunnelHeader), id, seq));
164
165 valid = handleEchoData(*header, dataLength - sizeof(TunnelHeader), ip, reply, id, seq);
166 }
167
168 if (!valid && !reply && answerEcho)
169 {
170 memcpy(echo->sendPayloadBuffer(), echo->receivePayloadBuffer(), dataLength);
171 echo->send(dataLength, ip, true, id, seq);
172 }
173 }
174 }
175
176 // data from tun
177 if (FD_ISSET(tun->getFd(), &fs))
178 {
179 uint32_t sourceIp, destIp;
180
181 int dataLength = tun->read(echoSendPayloadBuffer(), sourceIp, destIp);
182
183 if (dataLength == 0)
184 throw Exception("tunnel closed");
185
186 if (dataLength != -1)
187 handleTunData(dataLength, sourceIp, destIp);
188 }
189 }
190 }
191
stop()192 void Worker::stop()
193 {
194 alive = false;
195 }
196
dropPrivileges()197 void Worker::dropPrivileges()
198 {
199 if (uid <= 0 || privilegesDropped)
200 return;
201
202 #ifdef WIN32
203 throw Exception("dropping privileges not supported");
204 #else
205 syslog(LOG_INFO, "dropping privileges");
206
207 if (setgroups(0, NULL) == -1)
208 throw Exception("setgroups", true);
209
210 if (setgid(gid) == -1)
211 throw Exception("setgid", true);
212
213 if (setuid(uid) == -1)
214 throw Exception("setuid", true);
215
216 privilegesDropped = true;
217 #endif
218 }
219