UPnP issue solved in Overlayweaver

UPnP in Overlayweaver - かーねる・う゛いえむにっきの解決編。

デバッガで真面目に追ってみる。
先回書いたコードのUPnPAddressPortMapper.start()はUPnPAddressPortMapperのスレッドを作って実行するので、run()から見ていく。

	public void run() {
		UPnPManager upnp = null;
		try {
			upnp = UPnPManager.getInstance();
		}
		catch (NoClassDefFoundError e) {
			// clink*.jar is not found
			System.out.println("UPnP library is not found and UPnP NAT Traversal is disabled.");
			System.out.println("If you want the function, add clink*.jar to your classpath.");
			System.out.println();

			return;
		}

		upnp.start();

		if (!upnp.waitForDeviceFound(this.timeout)) {
			upnp.stop();
			return;

ここでifを越えられないとデバイスが見つからなかったという事ではいさようならな訳だが、実際ここでreturnしてしまっている。
何故waitForDeviceFound()がfalseを返すのか見ていこう。

	public boolean waitForDeviceFound(long timeout) {
		synchronized (this) {
			if (this.deviceFound()) return true;

			try {
				this.wait(timeout);
			}
			catch (InterruptedException e) { /*ignore*/ }
		}

		return this.deviceFound();
	}

この関数は、UPnPManagerでデバイスが発見されてdeviceFound() == trueになるのをtimeoutミリ秒までまっているようだ。
どうしたらdeviceFound() == trueになるのか。

	private boolean deviceFound() {
		return (UPnPManager.this.dev != null && UPnPManager.this.serv != null);
	}

devとservがnullじゃなければ良いらしい。
では、それはどこで設定されるのか。
UPnPAddressPortManagerが叩いていたstart()を見てみる。

	public boolean start() {
		this.cp.addDeviceChangeListener(new DevChgListener());
		boolean ret = this.cp.start();

		logger.log(Level.INFO, "UPnP manager started.");

		return ret;
	}

ここでDevChgListenerというクラスがnewされていて、これがイベントハンドラになっているらしい。
今度はこのクラスをみていく。

	private class DevChgListener implements DeviceChangeListener {
		public void deviceAdded(Device dev) {
			if (UPnPManager.this.deviceFound()) {
				// no NAT device has been found
				return;
			}

			if (!(dev.getDeviceType().equals(UPnPManager.ROUTER_DEV) && dev.isRootDevice())) {
				return;
			}

			UPnPManager.this.dev = dev;  /* ←ここや! */

			for (Object o1: dev.getDeviceList()) {
				Device d1 = (Device)o1;

				if (!d1.getDeviceType().equals(UPnPManager.WAN_DEV))
					continue;

				for (Object o2: d1.getDeviceList()) {
					Device d2 = (Device)o2;

					if (!d2.getDeviceType().equals(UPnPManager.WANCON_DEV))
						continue;

					UPnPManager.this.serv = d2.getService(UPnPManager.WANIPCON_SERV);  /* ←ここや! */

					logger.log(Level.INFO, "UPnP device found: " + dev.getFriendlyName());

					// notify
					synchronized (UPnPManager.this) {
						UPnPManager.this.notifyAll();
					}
				}
			}
		}

二カ所←を書いた所でそれぞれdevとservを設定してる。
早速デバッガをかけてみると、devは無事Deviceクラスのインスタンスが設定されているが、servの方は通過後もnullのままになってしまっている。
何故だろう。
UPnPManager.WANIPCON_SERVは"urn:schemas-upnp-org:service:WANIPConnection:1"の事らしい。
そういう名前のServiceを取得しにいっているという事のようだ。
分からなかったので、取り敢えず出来る事も無いしぼ〜っとWiresharkでパケットダンプを取ってみた。

M-SEARCH * HTTP/1.1
ST: upnp:rootdevice
MX: 3
MAN: "ssdp:discover"
HOST: 239.255.255.250:1900

最初の探索パケット。

HTTP/1.1 200 OK
Cache-Control:max-age=1800
Location:http://192.168.1.1:1900/upnp/service/des_ppp.xml
Server:NT/5.0 UPnP/1.0
ST:upnp:rootdevice
EXT:
USN:uuid:66616e20-7a68-616e-2020000a7925f0d8::upnp:rootdevice

それに対するレスポンス。
えっと、ここのNT 5.0ってWindows 2000の事です?
そう答えるという仕様なのか?これは。
なんか大嘘言ってる気がしてならないなぁ。

GET /upnp/service/des_ppp.xml HTTP/1.1
User-Agent: Java/1.6.0_0
Host: 192.168.1.1:1900
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

レスポンス帰ってきた相手へなんかXMLデータを取得しにいってる。

HTTP/1.1 200 OK
Server: UPnP/1.0 UPnP-Device-Host/1.0
Connection: close
Content-type: text/xml

<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<URLBase>http://192.168.1.1:1900</URLBase>
<device>
<deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
<friendlyName>WLBAR-54GT</friendlyName>
<manufacturer></manufacturer>
<manufacturerURL></manufacturerURL>
<modelDescription>WLBAR-54GT</modelDescription>
<modelName>WLBAR-54GT</modelName>
<UDN>uuid:66616e20-7a68-616e-2020000a7925f0d8</UDN>
<UPC>00000-00001</UPC>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
<serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>
<controlURL>/upnp/service/Layer3Forwarding</controlURL>
<eventSubURL>/upnp/service/Layer3Forwarding</eventSubURL>
<SCPDURL>/upnp/service/L3Frwd.xml</SCPDURL>
</service>
</serviceList>
<deviceList>
<device>
<deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
<friendlyName>WLBAR-54GT</friendlyName>
<manufacturer></manufacturer>
<manufacturerURL></manufacturerURL>
<modelDescription>WLBAR-54GT</modelDescription>
<modelName>WLBAR-54GT</modelName>
<modelNumber>1</modelNumber>
<modelURL></modelURL>
<serialNumber>0000001</serialNumber>
<UDN>uuid:68652070-696e-6720-2020000a7925f0d8</UDN>
<UPC>00000-00001</UPC>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
<serviceId>urn:upnp-org:serviceId:WANCommonInterfaceConfig</serviceId>
<controlURL>/upnp/service/WANCommonInterfaceConfig</controlURL>
<eventSubURL>/upnp/service/WANCommonInterfaceConfig</eventSubURL>
<SCPDURL>/upnp/service/WANCICfg.xml</SCPDURL>
</service>
</serviceList>
<deviceList>
<device>
<deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
<friendlyName>WLBAR-54GT</friendlyName>
<manufacturer></manufacturer>
<manufacturerURL></manufacturerURL>
<modelDescription>WLBAR-54GT</modelDescription>
<modelName>WLBAR-54GT</modelName>
<modelNumber>1</modelNumber>
<modelURL></modelURL>
<serialNumber>0000001</serialNumber>
<UDN>uuid:6661207a-6861-6e20-2020000a7925f0d8</UDN>
<UPC>00000-00001</UPC>
<serviceList><service>                              <serviceType>urn:schemas-upnp-org:service:WANPPPConnection:1</serviceType>                              <serviceId>urn:upnp-org:serviceId:WANPPPConn1</serviceId>                              <SCPDURL>/upnp/service/WANPPPCn.xml</SCPDURL>                              <controlURL>/upnp/service/WANPPPConnection?1</controlURL>                              <eventSubURL>/upnp/service/WANPPPConnection?1</eventSubURL>                            </service></serviceList>
</device>
</deviceList>
</device>
</deviceList>
<presentationURL>http://192.168.1.1</presentationURL>
</device>
</root>

serviceListを見て気がついたのだが、WANIPConnectionって項目がねぇ。
が、WANPPPConnectionという項目ならある。

もしかして、このルータではIPではなくPPPという名前で取得しにいくのが正しいんじゃないのか?

と思って

	private final static String WANPPPCON_SERV =
		"urn:schemas-upnp-org:service:WANPPPConnection:1";

という変数を宣言し、

					UPnPManager.this.serv = d2.getService(UPnPManager.WANIPCON_SERV);
					if (UPnPManager.this.serv == null)
						UPnPManager.this.serv = d2.getService(UPnPManager.WANPPPCON_SERV);

と書き換えてみたらあっさり動いたわ〜い\(^o^)/

・・・といった所で今日は力尽きました。
あれ、DHT弄ってたつもりがいつの間にかUPnPに・・・。


注:
こういうときはプロトコル仕様を良く読むべきです。
あんまり適当にやってるとものすごい痛い目に遭いかねません。
でもめんどくさいし眠いんだもん。

参考文献 http://hikarukaru.jugem.cc/?eid=45