File: RewriteAddress.pm

package info (click to toggle)
tiarra 20100212-4
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 2,732 kB
  • ctags: 1,712
  • sloc: perl: 32,032; lisp: 193; sh: 109; makefile: 10
file content (342 lines) | stat: -rw-r--r-- 8,973 bytes parent folder | download | duplicates (4)
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# -----------------------------------------------------------------------------
# $Id: RewriteAddress.pm 15771 2008-07-13 23:55:21Z drry $
# -----------------------------------------------------------------------------
# Rewrite ip address of CTCP DCC issued by client
# -----------------------------------------------------------------------------
package CTCP::DCC::RewriteAddress;
use strict;
use warnings;
use base qw(Module);
use Multicast;
use CTCP;
use Tiarra::Resolver;
use Module::Use qw(Tools::HTTPClient);
use Tools::HTTPClient;


sub new {
    my $this = shift->SUPER::new(@_);

    $this->{permit_types} = [map uc, split /\s+/,
			     ($this->config->type || 'CHAT SEND')];
    $this->{resolvers} = [map lc, split /\s+/, $this->config->resolver];

    return $this;
}

sub message_arrived {
    my ($this,$msg,$sender) = @_;

    if ($sender->isa('IrcIO::Client') &&
	$msg->command eq 'PRIVMSG' &&
	    !defined $msg->nick) {

	my $text = $msg->param(1);
	foreach my $ctcp (CTCP->extract_from_text("$text")) {
	    if ($ctcp =~ m|^DCC (\S*) (.*)$|) {
		my ($type, $params) = (uc($1), $2);
		next unless grep { $type eq $_ } @{$this->{permit_types}};
		my $result = $this->rewrite_dcc(
		    $msg->clone, $type, $params, $sender);
		next unless $result;
		my $encoded_ctcp = CTCP->make_text($ctcp);
		$text =~ s/\Q$encoded_ctcp\E//;
	    }
	}
	if ($text) {
	    $msg->param(1, $text);
	    return $msg;
	} else {
	    return undef;
	}
    }

    $msg;
}

our %resolvers = (
    'server-socket' => {
	resolver => sub {
	    my ($this, $actions, $sender, $conf, $msg, $addr, $port) = @_;
	    my $sock = ($this->_runloop->networks_list)[0]->sock;
	    return undef unless defined $sock;

	    $actions->{resolve}->(
		nameinfo => $sock->sockname,
		sub {
		    $actions->{callback}->(shift->answer_data->[0],
					  $port);
		});
	},
    },
    'client-socket' => {
	resolver => sub {
	    my ($this, $actions, $sender, $conf, $msg, $addr, $port) = @_;
	    my $sock = $sender->sock;
	    return undef unless defined $sock;

	    $actions->{resolve}->(
		nameinfo => $sock->peername,
		sub {
		    $actions->{callback}->(shift->answer_data->[0],
					  $port);
		});
	    1;
	},
    },
    'dns' => {
	resolver => sub {
	    my ($this, $actions, $sender, $conf, $msg, $addr, $port) = @_;

	    $actions->{resolve}->(
		addr => $conf->host,
		sub {
		    $actions->{callback}->(shift->answer_data->[0],
					  $port);
		});
	},
    },
    'http' => {
	resolver => sub{
	    my ($this, $actions, $sender, $conf, $msg, $addr, $port) = @_;

	    my $regex = "".$conf->regex; # regex to string
	    my $callback = sub {
		my $resp = shift;
		$actions->{step}->(
		    sub {
			return undef unless ref($resp);
			if ($resp->{Content} !~ /$regex/) {
			    $this->_runloop->notify_warn(
				__PACKAGE__." http: regex not match: $regex");
			    #::printmsg("http: content: $resp->{Content}");
			    return undef;
			}
			$actions->{callback}->($1, $port);
			1;
		    });
	    };
	    Tools::HTTPClient->new(
		Method => 'GET',
		Url => $conf->url,
		Debug => 1,
	       )->start($callback);
	    1;
	},
    },
   );

sub intaddr_to_octet {
    my $intaddr = shift;
    my $tail = $intaddr;
    my @ret;
    foreach (0..3) {
	unshift(@ret, $tail % 256);
	$tail /= 256;
    }
    join('.', @ret);
}

sub octet_to_intaddr {
    my $ret = 0;
    foreach (split /\./, shift) {
	$ret *= 256;
	$ret += $_;
    }
    $ret;
}

# $this->get_dcc_address_port($msg, $msg_sender, $dcc_addr, $dcc_port,
#                             $callback, @resolvers)
# callback:
#   sub {
#       my ($addr, $port) = @_;
#       $addr = default_addr unless defined $addr;
#       $port = default_port unless defined $port;
#       ...
#   }
sub get_dcc_address_port {
    my ($this, $msg, $sender, $addr, $port, $callback, @resolvers) = @_;
    my $resolver;
    my $step;
    my $next;

    # resolving step wrapper.
    # $actions->{step}->(sub { ... }, @args_to_closure)
    #   closure return undef (or on error): try next method.
    #   otherwise wrapper return with closure return value.
    $step = sub {
	my $ret = eval { shift->(@_) };
	if (!defined $ret) {
	    if ($@) {
		$this->_runloop->notify_warn(
		    __PACKAGE__." $resolver: error occurred: $@");
	    }
	    $this->_runloop->notify_warn(
		__PACKAGE__." $resolver: cannot resolved. try next method.");
	    $next->();
	} else {
	    $ret;
	}
    };

    # Tiarra::Resolver->resolve wrapper.
    # $actions->{resolve}->($type => $data, sub { ... }, @args_to_callback);
    #   1. resolve answer status is not OK, try next method.
    #   2. call callback with args: (@args_to_callback, $resolved).
    #   3. callback return undef, try next method.
    #   4. otherwise wrapper return with callback return value
    my $resolve = sub {
	my $type = shift;
	my $data = shift;
	my $callback = shift;
	my @args = @_;

	Tiarra::Resolver->resolve(
	    $type => $data,
	    sub {
		my $resolved = shift;
		my $ret = eval {
		    if ($resolved->answer_status ne $resolved->ANSWER_OK) {
			$this->_runloop->notify_warn(
			    __PACKAGE__." resolver: $type/$data: return not OK");
			undef; # next method
		    } else {
			$callback->(@args, $resolved, @_);
		    }
		};
		if (!defined $ret) {
		    if ($@) {
			$this->_runloop->notify_warn(
			    __PACKAGE__." $resolver: error occurred: $@");
		    }
		    $this->_runloop->notify_warn(
			__PACKAGE__." $resolver: cannot resolved. try next method.");
		    $next->();
		} else {
		    $ret;
		}
	    });
	1;
    };

    my $actions = {
	callback => $callback,
	step => $step,
	resolve => $resolve,
    };

    $next = sub {
	if (!@resolvers) {
	    ## FIXME: on cannot resolve
	    $this->_runloop->notify_warn(
		__PACKAGE__." cannot resolve address at all");
	    $callback->();
	}
	$resolver = shift(@resolvers);
	$step->(sub { $resolvers{$resolver}->{resolver}->(
	    $this, $actions, $sender,
	    $this->config->get($resolver, 'block'), $msg, $addr, $port); });
    };

    $next->();
}

sub rewrite_dcc {
    my ($this, $msg, $type, $param, $sender) = @_;
    if ($param !~ /^(\S+) ([\d.]+) (\S+)(.*)$/) {
	return undef;
    }

    my ($arg, $addr, $port, $trail) = ($1, $2, $3, $4);

    $addr = intaddr_to_octet($addr);
    my $send_dcc = sub {
	my ($addr, $port) = @_;
	$addr = octet_to_intaddr($addr);
	$msg->param(1, CTCP->make_text("DCC $type $arg $addr $port$trail"));
	Multicast::from_client_to_server($msg, $sender);
	1;
    };

    my $callback = sub {
	my ($newaddr, $newport) = @_;
	$addr = $newaddr if $newaddr;
	$port = $newport if $newport;

	$send_dcc->($addr, $port);
    };
    $this->get_dcc_address_port(
	$msg, $sender, $addr, $port, $callback, @{$this->{resolvers}});

}

1;

=pod
info: クライアントが送信した CTCP DCC のアドレスを変換する。
default: off
section: important

# CTCP DCC に指定されているアドレスを、 Tiarra で取得したものに
# 書き換えます。(EXPERIMENTAL)
#
# IPv4 のみサポートしています。
#
# このモジュールは一旦 CTCP DCC メッセージを破棄するので、
# 別のクライアントには送信されません。

# 変換する DCC タイプ。 [デフォルト値: CHAT SEND]
type: CHAT SEND

# 変換用アドレスの取得方法を選択する。デフォルト値はありません。
# 以下の取得方法(server-socket client-socket dns http)から
# 必要なもの(複数可)を指定してください。
resolver: client-socket server-socket dns http


# 取得方法と設定
# なにも設定がないときはブロック自体を省略することもできます。

server-socket {
  # サーバソケットのローカルアドレスを取ります。
  # client <-> tiarra[this address] <-> server
}

client-socket {
  # クライアントソケットのリモートアドレスを取ります。
  # client [this address]<-> Tiarra <-> server
}

dns {
  # DNS を引いて決定します。IPアドレスの指定も可能です。
  host: example.com
}

http {
  # 現状では単純な GET しかサポートしていません。

  # アクセス先 URL
  url: http://checkip.dyndns.org/

  # IP アドレス取得用 regex
  regex: Current IP Address: (\d+\.\d+\.\d+\.\d+)
}

# リゾルバの選び方
#
#  * Tiarra を動作させているサーバとインターネットの間にルータ等があり、
#    グローバルアドレスがない場合
#      *-socket は役に立ちません。 http を利用してください。
#      適当な DDNS を持っていればdns も良いでしょう。
#
#  * Tiarra がレンタルサーバなどLAN上にないサーバで動作している場合
#      server-socket, http は役に立ちません。
#      client-socket がお勧めです。
#
#  * Tiarra がLAN上にあり、グローバルアドレスのついているホストで
#    動作している場合
#      client-socket は役に立ちません。
#      server-socket がお勧めです。

=cut