From 3b817a2b854065af23c9ec8e849150e6930f51e9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 6 Oct 2018 16:04:39 -0700 Subject: map-server: Add maps proxy server This creates a map proxy server to handle the new Google Maps API requirements Signed-off-by: Keith Packard --- map-server/altos-mapd/AltosMapdClient.java | 148 +++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 map-server/altos-mapd/AltosMapdClient.java (limited to 'map-server/altos-mapd/AltosMapdClient.java') diff --git a/map-server/altos-mapd/AltosMapdClient.java b/map-server/altos-mapd/AltosMapdClient.java new file mode 100644 index 00000000..fb0c08e6 --- /dev/null +++ b/map-server/altos-mapd/AltosMapdClient.java @@ -0,0 +1,148 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +package altosmapd; + +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.io.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMapdClient extends Thread implements AltosMapStoreListener { + private Socket socket; + private AltosJson request; + private AltosJson reply; + + private void set_status(int status) { + reply.put("status", status); + } + + private void set_filename(String filename) { + reply.put("filename", filename); + + } + + private void set_content_type(String content_type) { + reply.put("content_type", content_type); + } + + private String content_type(File file) { + String content_type = "application/octet-stream"; + String basename = file.getName(); + if (basename.endsWith(".jpg")) + content_type = "image/jpeg"; + if (basename.endsWith(".png")) + content_type = "image/png"; + return content_type; + } + + private void set_file(File file) { + set_filename(file.getAbsolutePath()); + set_content_type(content_type(file)); + } + + private Semaphore store_ready; + + public void notify_store(AltosMapStore map_store, int status) { + if (status != AltosMapTile.fetching) + store_ready.release(); + } + + public void run() { + reply = new AltosJson(); + try { + request = AltosJson.fromInputStream(socket.getInputStream()); + + double lat = request.get_double("lat", AltosLib.MISSING); + double lon = request.get_double("lon", AltosLib.MISSING); + int zoom = request.get_int("zoom", AltosLib.MISSING); + String addr = request.get_string("remote_addr", null); + + if (lat == AltosLib.MISSING || + lon == AltosLib.MISSING || + zoom == AltosLib.MISSING || + addr == null) + { + set_status(400); + } else { + store_ready = new Semaphore(0); + + System.out.printf("Fetching tile for %g %g %d\n", lat, lon, zoom); + + AltosMapStore map_store = AltosMapStore.get(new AltosLatLon(lat, lon), + zoom, + AltosMapd.maptype, + AltosMapd.px_size, + AltosMapd.scale); + int status; + + if (map_store == null) { + System.out.printf("no store?\n"); + status = AltosMapTile.failed; + } else { + map_store.add_listener(this); + + System.out.printf("Waiting for tile\n"); + + try { + store_ready.acquire(); + } catch (Exception ie) { + } + + status = map_store.status(); + } + + if (status == AltosMapTile.fetched || status == AltosMapTile.loaded) { + set_status(200); + set_file(map_store.file); + } else if (status == AltosMapTile.failed) { + set_status(404); + } else if (status == AltosMapTile.fetching) { + set_status(408); + } else if (status == AltosMapTile.bad_request) { + set_status(400); + } else if (status == AltosMapTile.forbidden) { + set_status(403); + } else { + set_status(400); + } + } + } catch (Exception e) { + System.out.printf("client exception %s\n", e.toString()); + e.printStackTrace(System.out); + set_status(400); + + } finally { + try { + Writer writer = new PrintWriter(socket.getOutputStream()); + reply.write(writer); + writer.write('\n'); + writer.flush(); + } catch (IOException ie) { + } + try { + socket.close(); + } catch (IOException ie) { + } + System.out.printf("client done\n"); + } + } + + public AltosMapdClient(Socket socket) { + this.socket = socket; + start(); + } +} -- cgit v1.2.3 From e7c0bcd945f8365e86e99b9d450f3a3389fa0e66 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 6 Oct 2018 19:33:51 -0700 Subject: altos-mapd: Add geo-fencing for map requests. Add port and key arguments Map requests are now limited to a region within 17km of any registered launch site. The --port argument allows the listen port to be changed. The --key argument provides the name of a file containing the Google maps API key. Signed-off-by: Keith Packard --- map-server/altos-mapd/AltosMapd.java | 153 ++++++++++++++++++++++++++--- map-server/altos-mapd/AltosMapdClient.java | 3 + map-server/altos-mapd/AltosMapdServer.java | 21 +--- 3 files changed, 146 insertions(+), 31 deletions(-) (limited to 'map-server/altos-mapd/AltosMapdClient.java') diff --git a/map-server/altos-mapd/AltosMapd.java b/map-server/altos-mapd/AltosMapd.java index 9c39adb1..923c713e 100644 --- a/map-server/altos-mapd/AltosMapd.java +++ b/map-server/altos-mapd/AltosMapd.java @@ -16,12 +16,15 @@ package altosmapd; import java.net.*; import java.io.*; +import java.text.*; +import java.util.*; +import java.util.concurrent.*; import org.altusmetrum.altoslib_13.*; -public class AltosMapd { +public class AltosMapd implements AltosLaunchSiteListener { - public final static int port = 16717; + public static int port = 16717; public final static int maptype = AltosMap.maptype_hybrid; @@ -29,27 +32,149 @@ public class AltosMapd { public final static int scale = 1; - public static void main(final String[] args) { + public static double valid_radius = 17000; /* 17km */ + + public String map_dir = null; + public String launch_sites_file = null; + public String key_file = null; + + public void usage() { + System.out.printf("usage: altos-mapd [--mapdir ]\n" + + " [--radius [--port ] [--key ]\n"); + System.exit(1); + } + + private static Semaphore launch_sites_ready; + + private static List launch_sites; + + public void notify_launch_sites(List sites) { + synchronized (launch_sites_ready) { + if (sites != null) { + launch_sites = sites; + launch_sites_ready.release(); + } + } + } + + public static boolean check_lat_lon(double lat, double lon) { + synchronized (launch_sites_ready) { + if (launch_sites == null) { + try { + launch_sites_ready.acquire(); + } catch (InterruptedException ie) { + return false; + } + } + } + if (launch_sites == null) { + System.out.printf("No launch site data available, refusing all requests\n"); + return false; + } + + for (AltosLaunchSite site : launch_sites) { + AltosGreatCircle gc = new AltosGreatCircle(site.latitude, site.longitude, + lat, lon); + if (gc.distance <= valid_radius) + return true; + } + + return false; + } - AltosMapdServer server = new AltosMapdServer(port); + AltosMapdServer server; + + public void process(String[] args) { AltosPreferences.init(new AltosMapdPreferences()); - if (args.length < 1) { - System.out.printf("usage: altos-mapd \n"); - System.exit(1); + int skip = 1; + for (int i = 0; i < args.length; i += skip) { + skip = 1; + if (args[i].equals("--mapdir") && i < args.length-1) { + map_dir = args[i+1]; + skip = 2; + } else if (args[i].equals("--launch-sites") && i < args.length-1) { + launch_sites_file = args[i+1]; + skip = 2; + } else if (args[i].equals("--radius") && i < args.length-1) { + try { + valid_radius = AltosParse.parse_double_locale(args[i+1]); + } catch (ParseException pe) { + usage(); + } + skip = 2; + } else if (args[i].equals("--port") && i < args.length-1) { + try { + port = AltosParse.parse_int(args[i+1]); + } catch (ParseException pe) { + usage(); + } + skip = 2; + } else if (args[i].equals("--key") && i < args.length-1) { + key_file = args[i+1]; + skip = 2; + } else { + usage(); + } } - AltosPreferences.mapdir = new File(args[0]); + if (map_dir == null) + usage(); + + if (key_file != null) { + try { + BufferedReader key_reader = new BufferedReader(new FileReader(key_file)); + + String line = key_reader.readLine(); + if (line == null || line.length() != 39) { + System.out.printf("%s: invalid contents %d \"%s\"\n", + key_file, line.length(), line); + usage(); + } + key_reader.close(); + AltosMapStore.google_maps_api_key = line; + } catch (Exception e) { + System.out.printf("%s: %s\n", key_file, e.toString()); + usage(); + } + } + + AltosPreferences.mapdir = new File(map_dir); + + if (launch_sites_file != null) + AltosLaunchSites.launch_sites_url = "file://" + launch_sites_file; + + launch_sites_ready = new Semaphore(0); + + new AltosLaunchSites(this); + + try { + server = new AltosMapdServer(port); + } catch (IOException ie) { + System.out.printf("Cannot bind to port %d: %s\n", port, ie.toString()); + usage(); + } for (;;) { - Socket client = server.accept(); - if (client == null) { - System.out.printf("accept failed\n"); - continue; + try { + Socket client = server.accept(); + if (client == null) { + System.out.printf("accept failed\n"); + continue; + } + System.out.printf("got client\n"); + new AltosMapdClient(client); + } catch (Exception e) { + System.out.printf("Exception %s\n", e.toString()); } - System.out.printf("got client\n"); - new AltosMapdClient(client); } } + + public void AltosMapd() { + } + + public static void main(final String[] args) { + new AltosMapd().process(args); + } } diff --git a/map-server/altos-mapd/AltosMapdClient.java b/map-server/altos-mapd/AltosMapdClient.java index fb0c08e6..70ceae82 100644 --- a/map-server/altos-mapd/AltosMapdClient.java +++ b/map-server/altos-mapd/AltosMapdClient.java @@ -77,7 +77,10 @@ public class AltosMapdClient extends Thread implements AltosMapStoreListener { addr == null) { set_status(400); + } else if (!AltosMapd.check_lat_lon(lat, lon)) { + set_status(403); /* Forbidden */ } else { + store_ready = new Semaphore(0); System.out.printf("Fetching tile for %g %g %d\n", lat, lon, zoom); diff --git a/map-server/altos-mapd/AltosMapdServer.java b/map-server/altos-mapd/AltosMapdServer.java index 68b427f0..749edbc7 100644 --- a/map-server/altos-mapd/AltosMapdServer.java +++ b/map-server/altos-mapd/AltosMapdServer.java @@ -15,23 +15,10 @@ package altosmapd; import java.net.*; +import java.io.*; -public class AltosMapdServer { - ServerSocket socket; - - public Socket accept() { - try { - return socket.accept(); - } catch (Exception e) { - return null; - } - } - - public AltosMapdServer(int port) { - try { - socket = new ServerSocket(port, 5, InetAddress.getLoopbackAddress()); - } catch (Exception e) { - socket = null; - } +public class AltosMapdServer extends ServerSocket { + public AltosMapdServer(int port) throws IOException { + super(port, 5, InetAddress.getLoopbackAddress()); } } -- cgit v1.2.3 From 02adfb13297f7dffcd8edcdd3b8747cf52b2a70b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 6 Oct 2018 22:27:20 -0600 Subject: altos-mapd: Clean up log messages Generate a single log message per request with remote address, parameters and result code. Signed-off-by: Keith Packard --- map-server/altos-mapd/AltosMapdClient.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'map-server/altos-mapd/AltosMapdClient.java') diff --git a/map-server/altos-mapd/AltosMapdClient.java b/map-server/altos-mapd/AltosMapdClient.java index 70ceae82..09833363 100644 --- a/map-server/altos-mapd/AltosMapdClient.java +++ b/map-server/altos-mapd/AltosMapdClient.java @@ -26,7 +26,10 @@ public class AltosMapdClient extends Thread implements AltosMapStoreListener { private AltosJson request; private AltosJson reply; + private int http_status; + private void set_status(int status) { + http_status = status; reply.put("status", status); } @@ -83,8 +86,6 @@ public class AltosMapdClient extends Thread implements AltosMapStoreListener { store_ready = new Semaphore(0); - System.out.printf("Fetching tile for %g %g %d\n", lat, lon, zoom); - AltosMapStore map_store = AltosMapStore.get(new AltosLatLon(lat, lon), zoom, AltosMapd.maptype, @@ -93,13 +94,10 @@ public class AltosMapdClient extends Thread implements AltosMapStoreListener { int status; if (map_store == null) { - System.out.printf("no store?\n"); status = AltosMapTile.failed; } else { map_store.add_listener(this); - System.out.printf("Waiting for tile\n"); - try { store_ready.acquire(); } catch (Exception ie) { @@ -123,6 +121,9 @@ public class AltosMapdClient extends Thread implements AltosMapStoreListener { set_status(400); } } + System.out.printf("%s: %.6f %.6f %d status %d\n", + addr, lat, lon, zoom, http_status); + } catch (Exception e) { System.out.printf("client exception %s\n", e.toString()); e.printStackTrace(System.out); @@ -140,7 +141,6 @@ public class AltosMapdClient extends Thread implements AltosMapStoreListener { socket.close(); } catch (IOException ie) { } - System.out.printf("client done\n"); } } -- cgit v1.2.3 From 6f911b92652de890ebff67b3b28f21a36f300ebf Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 7 Oct 2018 10:03:31 -0700 Subject: altos-mapd: Handle clients failing to send valid JSON Just bail if the data we receive doesn't generate a json value. Signed-off-by: Keith Packard --- map-server/altos-mapd/AltosMapdClient.java | 95 ++++++++++++++++-------------- 1 file changed, 50 insertions(+), 45 deletions(-) (limited to 'map-server/altos-mapd/AltosMapdClient.java') diff --git a/map-server/altos-mapd/AltosMapdClient.java b/map-server/altos-mapd/AltosMapdClient.java index 09833363..19174088 100644 --- a/map-server/altos-mapd/AltosMapdClient.java +++ b/map-server/altos-mapd/AltosMapdClient.java @@ -69,61 +69,66 @@ public class AltosMapdClient extends Thread implements AltosMapStoreListener { try { request = AltosJson.fromInputStream(socket.getInputStream()); - double lat = request.get_double("lat", AltosLib.MISSING); - double lon = request.get_double("lon", AltosLib.MISSING); - int zoom = request.get_int("zoom", AltosLib.MISSING); - String addr = request.get_string("remote_addr", null); - - if (lat == AltosLib.MISSING || - lon == AltosLib.MISSING || - zoom == AltosLib.MISSING || - addr == null) - { + if (request == null) { set_status(400); - } else if (!AltosMapd.check_lat_lon(lat, lon)) { - set_status(403); /* Forbidden */ + System.out.printf("client failed %d\n", http_status); } else { - store_ready = new Semaphore(0); + double lat = request.get_double("lat", AltosLib.MISSING); + double lon = request.get_double("lon", AltosLib.MISSING); + int zoom = request.get_int("zoom", AltosLib.MISSING); + String addr = request.get_string("remote_addr", null); - AltosMapStore map_store = AltosMapStore.get(new AltosLatLon(lat, lon), - zoom, - AltosMapd.maptype, - AltosMapd.px_size, - AltosMapd.scale); - int status; - - if (map_store == null) { - status = AltosMapTile.failed; + if (lat == AltosLib.MISSING || + lon == AltosLib.MISSING || + zoom == AltosLib.MISSING || + addr == null) + { + set_status(400); + } else if (!AltosMapd.check_lat_lon(lat, lon)) { + set_status(403); /* Forbidden */ } else { - map_store.add_listener(this); - try { - store_ready.acquire(); - } catch (Exception ie) { - } + store_ready = new Semaphore(0); - status = map_store.status(); - } + AltosMapStore map_store = AltosMapStore.get(new AltosLatLon(lat, lon), + zoom, + AltosMapd.maptype, + AltosMapd.px_size, + AltosMapd.scale); + int status; - if (status == AltosMapTile.fetched || status == AltosMapTile.loaded) { - set_status(200); - set_file(map_store.file); - } else if (status == AltosMapTile.failed) { - set_status(404); - } else if (status == AltosMapTile.fetching) { - set_status(408); - } else if (status == AltosMapTile.bad_request) { - set_status(400); - } else if (status == AltosMapTile.forbidden) { - set_status(403); - } else { - set_status(400); + if (map_store == null) { + status = AltosMapTile.failed; + } else { + map_store.add_listener(this); + + try { + store_ready.acquire(); + } catch (Exception ie) { + } + + status = map_store.status(); + } + + if (status == AltosMapTile.fetched || status == AltosMapTile.loaded) { + set_status(200); + set_file(map_store.file); + } else if (status == AltosMapTile.failed) { + set_status(404); + } else if (status == AltosMapTile.fetching) { + set_status(408); + } else if (status == AltosMapTile.bad_request) { + set_status(400); + } else if (status == AltosMapTile.forbidden) { + set_status(403); + } else { + set_status(400); + } } + System.out.printf("%s: %.6f %.6f %d status %d\n", + addr, lat, lon, zoom, http_status); } - System.out.printf("%s: %.6f %.6f %d status %d\n", - addr, lat, lon, zoom, http_status); - } catch (Exception e) { System.out.printf("client exception %s\n", e.toString()); e.printStackTrace(System.out); -- cgit v1.2.3 From e7e1e805f1358052b33103a2ffdd4e9c435c8650 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 7 Oct 2018 14:08:30 -0600 Subject: altos-mapd: Check nearest portion of tile to launch site We limit tile queries to those within 17km of a known launch site. Change the check to look at the portion of the tile nearest each launch site, instead of only checking the center location of the tile. Signed-off-by: Keith Packard --- map-server/altos-mapd/AltosMapd.java | 46 ++++++++++++++++++++++++++++-- map-server/altos-mapd/AltosMapdClient.java | 2 +- 2 files changed, 45 insertions(+), 3 deletions(-) (limited to 'map-server/altos-mapd/AltosMapdClient.java') diff --git a/map-server/altos-mapd/AltosMapd.java b/map-server/altos-mapd/AltosMapd.java index b4105861..1be1655d 100644 --- a/map-server/altos-mapd/AltosMapd.java +++ b/map-server/altos-mapd/AltosMapd.java @@ -57,7 +57,23 @@ public class AltosMapd implements AltosLaunchSiteListener { } } - public static boolean check_lat_lon(double lat, double lon) { + private static boolean west_of(double a, double b) { + double diff = b - a; + + while (diff >= 360.0) + diff -= 360.0; + while (diff <= -360.0) + diff += 360.0; + + return diff >= 0; + } + + public static boolean check_lat_lon(double lat, double lon, int zoom) { + AltosMapTransform transform = new AltosMapTransform(px_size, px_size, zoom, new AltosLatLon(lat, lon)); + + AltosLatLon upper_left = transform.screen_lat_lon(new AltosPointInt(0, 0)); + AltosLatLon lower_right = transform.screen_lat_lon(new AltosPointInt(px_size, px_size)); + synchronized (launch_sites_ready) { if (launch_sites == null) { try { @@ -73,8 +89,34 @@ public class AltosMapd implements AltosLaunchSiteListener { } for (AltosLaunchSite site : launch_sites) { + + /* Figure out which point in the tile to + * measure to the site That's one of the edges + * or the site location depend on where the + * site is in relation to the tile + */ + + double check_lon; + + if (west_of(site.longitude, upper_left.lon)) + check_lon = upper_left.lon; + else if (west_of(lower_right.lon, site.longitude)) + check_lon = lower_right.lon; + else + check_lon = site.longitude; + + double check_lat; + + if (site.latitude < lower_right.lat) + check_lat = lower_right.lat; + else if (upper_left.lat < site.latitude) + check_lat = upper_left.lat; + else + check_lat = site.latitude; + AltosGreatCircle gc = new AltosGreatCircle(site.latitude, site.longitude, - lat, lon); + check_lat, check_lon); + if (gc.distance <= valid_radius) return true; } diff --git a/map-server/altos-mapd/AltosMapdClient.java b/map-server/altos-mapd/AltosMapdClient.java index 19174088..6c95da8f 100644 --- a/map-server/altos-mapd/AltosMapdClient.java +++ b/map-server/altos-mapd/AltosMapdClient.java @@ -85,7 +85,7 @@ public class AltosMapdClient extends Thread implements AltosMapStoreListener { addr == null) { set_status(400); - } else if (!AltosMapd.check_lat_lon(lat, lon)) { + } else if (!AltosMapd.check_lat_lon(lat, lon, zoom)) { set_status(403); /* Forbidden */ } else { -- cgit v1.2.3