]> git.tdb.fi Git - libs/net.git/blob - source/net/resolve.cpp
Move most platform-specific code into overlay directories
[libs/net.git] / source / net / resolve.cpp
1 #include "platform_api.h"
2 #include <msp/core/systemerror.h>
3 #include <msp/strings/format.h>
4 #include "sockaddr_private.h"
5 #include "socket.h"
6 #include "resolve.h"
7
8 using namespace std;
9
10 namespace {
11
12 void parse_host_serv(const string &str, string &host, string &serv)
13 {
14         if(str[0]=='[')
15         {
16                 string::size_type bracket = str.find(']');
17                 host = str.substr(1, bracket-1);
18                 string::size_type colon = str.find(':', bracket);
19                 if(colon!=string::npos)
20                         serv = str.substr(colon+1);
21         }
22         else
23         {
24                 string::size_type colon = str.find(':');
25                 if(colon!=string::npos)
26                 {
27                         host = str.substr(0, colon);
28                         serv = str.substr(colon+1);
29                 }
30                 else
31                         host = str;
32         }
33 }
34
35 }
36
37
38 namespace Msp {
39 namespace Net {
40
41 SockAddr *resolve(const string &host, const string &serv, Family family)
42 {
43         const char *chost = (host.empty() ? 0 : host.c_str());
44         const char *cserv = (serv.empty() ? 0 : serv.c_str());
45         int flags = 0;
46         if(host=="*")
47         {
48                 flags = AI_PASSIVE;
49                 chost = 0;
50         }
51
52         addrinfo hints = { flags, family_to_sys(family), 0, 0, 0, 0, 0, 0 };
53         addrinfo *res;
54
55         int err = getaddrinfo(chost, cserv, &hints, &res);
56         if(err==0)
57         {
58                 SockAddr::SysAddr sa;
59                 sa.size = res->ai_addrlen;
60                 const char *sptr = reinterpret_cast<const char *>(res->ai_addr);
61                 char *dptr = reinterpret_cast<char *>(&sa.addr);
62                 copy(sptr, sptr+res->ai_addrlen, dptr);
63                 SockAddr *addr = SockAddr::new_from_sys(sa);
64                 freeaddrinfo(res);
65                 return addr;
66         }
67         else
68 #ifdef _WIN32
69                 throw system_error("getaddrinfo", WSAGetLastError());
70 #else
71                 throw system_error("getaddrinfo", gai_strerror(err));
72 #endif
73 }
74
75 SockAddr *resolve(const string &str, Family family)
76 {
77         string host, serv;
78         parse_host_serv(str, host, serv);
79
80         return resolve(host, serv, family);
81 }
82
83
84 Resolver::Resolver():
85         event_disp(0),
86         next_tag(1)
87 {
88         thread.get_notify_pipe().signal_data_available.connect(sigc::mem_fun(this, &Resolver::task_done));
89 }
90
91 void Resolver::use_event_dispatcher(IO::EventDispatcher *ed)
92 {
93         if(event_disp)
94                 event_disp->remove(thread.get_notify_pipe());
95         event_disp = ed;
96         if(event_disp)
97                 event_disp->add(thread.get_notify_pipe());
98 }
99
100 unsigned Resolver::resolve(const string &host, const string &serv, Family family)
101 {
102         Task task;
103         task.tag = next_tag++;
104         task.host = host;
105         task.serv = serv;
106         task.family = family;
107         thread.add_task(task);
108         return task.tag;
109 }
110
111 unsigned Resolver::resolve(const string &str, Family family)
112 {
113         string host, serv;
114         parse_host_serv(str, host, serv);
115
116         return resolve(host, serv, family);
117 }
118
119 void Resolver::tick()
120 {
121         if(IO::poll(thread.get_notify_pipe(), IO::P_INPUT, Time::zero))
122                 task_done();
123 }
124
125 void Resolver::task_done()
126 {
127         char buf[64];
128         thread.get_notify_pipe().read(buf, sizeof(buf));
129
130         while(Task *task = thread.get_complete_task())
131         {
132                 if(task->addr)
133                         signal_address_resolved.emit(task->tag, *task->addr);
134                 else if(task->error)
135                         signal_resolve_failed.emit(task->tag, *task->error);
136                 thread.pop_complete_task();
137         }
138 }
139
140
141 Resolver::Task::Task():
142         tag(0),
143         family(UNSPEC),
144         addr(0),
145         error(0)
146 { }
147
148
149 Resolver::WorkerThread::WorkerThread():
150         Thread("Resolver"),
151         sem(1),
152         done(false)
153 {
154         launch();
155 }
156
157 Resolver::WorkerThread::~WorkerThread()
158 {
159         done = true;
160         sem.signal();
161         join();
162 }
163
164 void Resolver::WorkerThread::add_task(const Task &t)
165 {
166         MutexLock lock(queue_mutex);
167         bool was_starved = (queue.empty() || queue.back().is_complete());
168         queue.push_back(t);
169         if(was_starved)
170                 sem.signal();
171 }
172
173 Resolver::Task *Resolver::WorkerThread::get_complete_task()
174 {
175         MutexLock lock(queue_mutex);
176         if(!queue.empty() && queue.front().is_complete())
177                 return &queue.front();
178         else
179                 return 0;
180 }
181
182 void Resolver::WorkerThread::pop_complete_task()
183 {
184         MutexLock lock(queue_mutex);
185         if(!queue.empty() && queue.front().is_complete())
186         {
187                 delete queue.front().addr;
188                 delete queue.front().error;
189                 queue.pop_front();
190         }
191 }
192
193 void Resolver::WorkerThread::main()
194 {
195         bool wait = true;
196         while(!done)
197         {
198                 if(wait)
199                         sem.wait();
200                 wait = false;
201
202                 Task *task = 0;
203                 {
204                         MutexLock lock(queue_mutex);
205                         for(list<Task>::iterator i=queue.begin(); (!task && i!=queue.end()); ++i)
206                                 if(!i->is_complete())
207                                         task = &*i;
208                 }
209
210                 if(task)
211                 {
212                         try
213                         {
214                                 SockAddr *addr = Net::resolve(task->host, task->serv, task->family);
215                                 {
216                                         MutexLock lock(queue_mutex);
217                                         task->addr = addr;
218                                 }
219                         }
220                         catch(const runtime_error &e)
221                         {
222                                 MutexLock lock(queue_mutex);
223                                 task->error = new runtime_error(e);
224                         }
225                         notify_pipe.put(1);
226                 }
227                 else
228                         wait = true;
229         }
230 }
231
232 } // namespace Net
233 } // namespace Msp