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