Skip to content

Commit

Permalink
Fix integer overflow and parsing issues (#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
bilde2910 committed Dec 17, 2019
1 parent 3383c71 commit 388fd91
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 41 deletions.
4 changes: 3 additions & 1 deletion android/app/src/main/java/info/varden/hauk/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ public enum Constants {
// Regular expression for extracting a share ID from a URL when adopting a share.
public static final String REGEX_ADOPT_ID_FROM_LINK = "\\?([A-Za-z0-9-]+)";

// Default date format.
// Formatting and input validation.
public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss z";
public static final int PORT_MIN = 0;
public static final int PORT_MAX = 65536;

// Keys for intent extras.
public static final String EXTRA_SHARE = "share";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
import info.varden.hauk.R;
import info.varden.hauk.system.preferences.PreferenceHandler;
import info.varden.hauk.system.preferences.ui.listener.CascadeBindListener;
import info.varden.hauk.system.preferences.ui.listener.CascadeChangeListener;
import info.varden.hauk.system.preferences.ui.listener.FloatBoundChangeListener;
import info.varden.hauk.system.preferences.ui.listener.HintBindListener;
import info.varden.hauk.system.preferences.ui.listener.InputTypeBindListener;
import info.varden.hauk.system.preferences.ui.listener.IntegerBoundChangeListener;
import info.varden.hauk.system.preferences.ui.listener.NightModeChangeListener;
import info.varden.hauk.system.preferences.ui.listener.ProxyPreferenceChangeListener;
import info.varden.hauk.utils.Log;

/**
* Settings activity that allows the user to change app preferences.
Expand Down Expand Up @@ -57,48 +61,49 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);

// Set InputType and other attributes for text edit boxes.
((EditTextPreference) manager.findPreference(Constants.PREF_SERVER_ENCRYPTED.getKey())).setOnBindEditTextListener(new CascadeBindListener(new EditTextPreference.OnBindEditTextListener[]{
new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI),
new HintBindListener(R.string.pref_cryptServer_hint)
}));
((EditTextPreference) manager.findPreference(Constants.PREF_USERNAME_ENCRYPTED.getKey())).setOnBindEditTextListener(new CascadeBindListener(new EditTextPreference.OnBindEditTextListener[]{
new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME),
new HintBindListener(R.string.pref_cryptUsername_hint)
}));
((EditTextPreference) manager.findPreference(Constants.PREF_PASSWORD_ENCRYPTED.getKey())).setOnBindEditTextListener(
new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)
);
((EditTextPreference) manager.findPreference(Constants.PREF_E2E_PASSWORD.getKey())).setOnBindEditTextListener(
new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)
);
((EditTextPreference) manager.findPreference(Constants.PREF_INTERVAL.getKey())).setOnBindEditTextListener(
new InputTypeBindListener(InputType.TYPE_CLASS_NUMBER)
);
((EditTextPreference) manager.findPreference(Constants.PREF_UPDATE_DISTANCE.getKey())).setOnBindEditTextListener(
new InputTypeBindListener(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL)
);
((EditTextPreference) manager.findPreference(Constants.PREF_CUSTOM_ID.getKey())).setOnBindEditTextListener(new CascadeBindListener(new EditTextPreference.OnBindEditTextListener[]{
new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE),
new HintBindListener(R.string.pref_requestLink_hint)
}));
((EditTextPreference) manager.findPreference(Constants.PREF_PROXY_HOST.getKey())).setOnBindEditTextListener(
new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI)
);
((EditTextPreference) manager.findPreference(Constants.PREF_PROXY_PORT.getKey())).setOnBindEditTextListener(
new InputTypeBindListener(InputType.TYPE_CLASS_NUMBER)
);
((EditTextPreference) manager.findPreference(Constants.PREF_CONNECTION_TIMEOUT.getKey())).setOnBindEditTextListener(
new InputTypeBindListener(InputType.TYPE_CLASS_NUMBER)
);
setTextEditParams(manager, Constants.PREF_SERVER_ENCRYPTED, new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI), new HintBindListener(R.string.pref_cryptServer_hint));
setTextEditParams(manager, Constants.PREF_USERNAME_ENCRYPTED, new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME), new HintBindListener(R.string.pref_cryptUsername_hint));
setTextEditParams(manager, Constants.PREF_PASSWORD_ENCRYPTED, new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD));
setTextEditParams(manager, Constants.PREF_E2E_PASSWORD, new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD));
setTextEditParams(manager, Constants.PREF_INTERVAL, new InputTypeBindListener(InputType.TYPE_CLASS_NUMBER));
setTextEditParams(manager, Constants.PREF_UPDATE_DISTANCE, new InputTypeBindListener(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL));
setTextEditParams(manager, Constants.PREF_CUSTOM_ID, new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE), new HintBindListener(R.string.pref_requestLink_hint));
setTextEditParams(manager, Constants.PREF_PROXY_HOST, new InputTypeBindListener(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI));
setTextEditParams(manager, Constants.PREF_PROXY_PORT, new InputTypeBindListener(InputType.TYPE_CLASS_NUMBER));
setTextEditParams(manager, Constants.PREF_CONNECTION_TIMEOUT, new InputTypeBindListener(InputType.TYPE_CLASS_NUMBER));

// Set value bounds checks.
setChangeListeners(manager, Constants.PREF_INTERVAL, new IntegerBoundChangeListener(1, Integer.MAX_VALUE));
setChangeListeners(manager, Constants.PREF_UPDATE_DISTANCE, new FloatBoundChangeListener(0.0F, Float.MAX_VALUE));
setChangeListeners(manager, Constants.PREF_PROXY_PORT, new IntegerBoundChangeListener(Constants.PORT_MIN, Constants.PORT_MAX));
setChangeListeners(manager, Constants.PREF_CONNECTION_TIMEOUT, new IntegerBoundChangeListener(1, Integer.MAX_VALUE));

// Set proxy settings disabled if proxy is set to default or none.
manager.findPreference(Constants.PREF_PROXY_TYPE.getKey()).setOnPreferenceChangeListener(new ProxyPreferenceChangeListener(new Preference[]{
setChangeListeners(manager, Constants.PREF_PROXY_TYPE, new ProxyPreferenceChangeListener(new Preference[]{
manager.findPreference(Constants.PREF_PROXY_HOST.getKey()),
manager.findPreference(Constants.PREF_PROXY_PORT.getKey())
}));

// Update night mode when its preference is changed.
manager.findPreference(Constants.PREF_NIGHT_MODE.getKey()).setOnPreferenceChangeListener(new NightModeChangeListener());
setChangeListeners(manager, Constants.PREF_NIGHT_MODE, new NightModeChangeListener());
}

private static void setTextEditParams(PreferenceManager manager, info.varden.hauk.system.preferences.Preference<?> preference, EditTextPreference.OnBindEditTextListener... listeners) {
EditTextPreference pref = manager.findPreference(preference.getKey());
if (pref != null) {
pref.setOnBindEditTextListener(new CascadeBindListener(listeners));
} else {
Log.wtf("Could not find setting for preference %s setting OnBindEditTextListener", preference); //NON-NLS
}
}

private static void setChangeListeners(PreferenceManager manager, info.varden.hauk.system.preferences.Preference<?> preference, Preference.OnPreferenceChangeListener... listeners) {
Preference pref = manager.findPreference(preference.getKey());
if (pref != null) {
pref.setOnPreferenceChangeListener(new CascadeChangeListener(listeners));
} else {
Log.wtf("Could not find setting for preference %s when setting OnPreferenceChangeListener", preference); //NON-NLS
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package info.varden.hauk.system.preferences.ui.listener;

import androidx.preference.Preference;

/**
* Preference change listener that cascades the change event to several
* {@link androidx.preference.Preference.OnPreferenceChangeListener}s.
*
* @author Marius Lindvall
*/
public final class CascadeChangeListener implements Preference.OnPreferenceChangeListener {
private final Preference.OnPreferenceChangeListener[] listeners;

public CascadeChangeListener(Preference.OnPreferenceChangeListener[] listeners) {
this.listeners = listeners.clone();
}

@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
for (Preference.OnPreferenceChangeListener listener : this.listeners) {
if (!listener.onPreferenceChange(preference, newValue)) return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package info.varden.hauk.system.preferences.ui.listener;

import androidx.preference.Preference;

import info.varden.hauk.utils.Log;

/**
* Bounds checking preference change listener that ensures the given value is between two floating
* point values (inclusive).
*
* @author Marius Lindvall
*/
public final class FloatBoundChangeListener implements Preference.OnPreferenceChangeListener {
private final float min;
private final float max;

public FloatBoundChangeListener(float min, float max) {
this.min = min;
this.max = max;
}

@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
float value = Float.parseFloat((String) newValue);
return value >= this.min && value <= this.max;
} catch (NumberFormatException ex) {
Log.e("Number %s is not a valid float", ex, newValue); //NON-NLS
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package info.varden.hauk.system.preferences.ui.listener;

import androidx.preference.Preference;

import info.varden.hauk.utils.Log;

/**
* Bounds checking preference change listener that ensures the given value is between two integer
* values (inclusive).
*
* @author Marius Lindvall
*/
public final class IntegerBoundChangeListener implements Preference.OnPreferenceChangeListener {
private final int min;
private final int max;

public IntegerBoundChangeListener(int min, int max) {
this.min = min;
this.max = max;
}

@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
int value = Integer.parseInt((String) newValue);
return value >= this.min && value <= this.max;
} catch (NumberFormatException ex) {
Log.e("Number %s is not a valid integer", ex, newValue); //NON-NLS
}
return false;
}
}
16 changes: 12 additions & 4 deletions android/app/src/main/java/info/varden/hauk/ui/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public void startSharing(@SuppressWarnings("unused") View view) {
String server = prefs.get(Constants.PREF_SERVER_ENCRYPTED).trim();
String username = prefs.get(Constants.PREF_USERNAME_ENCRYPTED).trim();
String password = prefs.get(Constants.PREF_PASSWORD_ENCRYPTED);
int duration = Integer.parseInt(((TextView) findViewById(R.id.txtDuration)).getText().toString());
int duration;
int interval = prefs.get(Constants.PREF_INTERVAL);
float minDistance = prefs.get(Constants.PREF_UPDATE_DISTANCE);
String customID = prefs.get(Constants.PREF_CUSTOM_ID).trim();
Expand All @@ -195,6 +195,17 @@ public void startSharing(@SuppressWarnings("unused") View view) {
boolean allowAdoption = ((Checkable) findViewById(R.id.chkAllowAdopt)).isChecked();
@SuppressWarnings("OverlyStrongTypeCast") int durUnit = ((Spinner) findViewById(R.id.selUnit)).getSelectedItemPosition();

try {
// Try to parse the duration.
duration = Integer.parseInt(((TextView) findViewById(R.id.txtDuration)).getText().toString());
// The backend takes duration in seconds, hence it must be converted.
duration = TimeUtils.timeUnitsToSeconds(duration, durUnit);
} catch (NumberFormatException | ArithmeticException ex) {
Log.e("Illegal duration value", ex); //NON-NLS
this.dialogSvc.showDialog(R.string.err_client, R.string.err_invalid_duration, this.uiResetTask);
return;
}

// Save connection preferences for next launch, so the user doesn't have to enter URL etc.
// every time.
Log.i("Updating connection preferences"); //NON-NLS
Expand All @@ -215,9 +226,6 @@ public void startSharing(@SuppressWarnings("unused") View view) {
assert mode != null;
server = server.endsWith("/") ? server : server + "/";

// The backend takes duration in seconds, so convert the minutes supplied by the user.
duration = TimeUtils.timeUnitsToSeconds(duration, durUnit);

SessionInitiationPacket.InitParameters initParams = new SessionInitiationPacket.InitParameters(server, username, password, duration, interval, minDistance, customID, e2ePass);
new ProxyHostnameResolverImpl(this, this.manager, this.uiResetTask, prefs, new SessionInitiationResponseHandlerImpl(), initParams, mode, allowAdoption, nickname, groupPin).resolve();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,21 @@ public static String secondsToTime(long seconds) {
return sb.toString();
}

public static int timeUnitsToSeconds(int scalar, int unit) {
public static int timeUnitsToSeconds(int scalar, int unit) throws ArithmeticException {
switch (unit) {
case Constants.DURATION_UNIT_MINUTES:
if (Integer.MAX_VALUE / SECONDS_PER_MINUTE < scalar)
throw new ArithmeticException(String.format("Integer will overflow when converting %d minutes to seconds", scalar));
return scalar * SECONDS_PER_MINUTE;

case Constants.DURATION_UNIT_HOURS:
if (Integer.MAX_VALUE / SECONDS_PER_HOUR < scalar)
throw new ArithmeticException(String.format("Integer will overflow when converting %d hours to seconds", scalar));
return scalar * SECONDS_PER_HOUR;

case Constants.DURATION_UNIT_DAYS:
if (Integer.MAX_VALUE / SECONDS_PER_DAY < scalar)
throw new ArithmeticException(String.format("Integer will overflow when converting %d days to seconds", scalar));
return scalar * SECONDS_PER_DAY;

default:
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<string name="err_missing_perms">Location permission is required to use this app.</string>
<string name="err_location_disabled">Location services are disabled. Please enable high-accuracy location services to share your location.</string>
<string name="err_server_not_configured">You haven\'t configured a backend yet. Please enter the details for your backend server in settings and try again.</string>
<string name="err_invalid_duration">The duration you entered is invalid. Please choose a different duration.</string>
<string name="err_connect">Connection error</string>
<string name="err_proxy_failure">An error occurred when resolving the hostname of the configured proxy: %s</string>
<string name="err_proxy_host_resolution">The IP address of the configured proxy (%s) could not be resolved!</string>
Expand Down

0 comments on commit 388fd91

Please sign in to comment.